DI 注入文件系统,web平台不支持System.IO.File

master
Cal 2023-12-05 20:11:38 +08:00
parent a02aeca6df
commit 09d8bc7ba8
15 changed files with 246 additions and 52 deletions

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Notes.Android;
class FileService : IFileService
{
public string Combine(string args0, string args1)
{
return Path.Combine(args0, args1);
}
public string GetRandomFileName()
{
return Path.GetRandomFileName();
}
public IEnumerable<string> EnumerateFiles(string path, string patten)
{
return Directory.EnumerateFiles(path, patten);
}
public string GetFileName(string filename)
{
return Path.GetFileName(filename);
}
public string ReadAllText(string filename)
{
return File.ReadAllText(filename);
}
public DateTime GetLastWriteTime(string filename)
{
return File.GetLastWriteTime(filename);
}
public bool ExistsFile(string filename)
{
return File.Exists(filename);
}
public void Delete(string path)
{
File.Delete(path);
}
public void WriteAllText(string path, string text)
{
File.WriteAllText(path, text);
}
}

View File

@ -3,6 +3,7 @@ using Android.Content.PM;
using Avalonia;
using Avalonia.Android;
using Avalonia.ReactiveUI;
using Notes.ViewModels;
namespace Notes.Android;
@ -16,6 +17,10 @@ public class MainActivity : AvaloniaMainActivity<App>
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
var services = new Services();
services.Register<IFileService>(new FileService());
App.services = services;
return base.CustomizeAppBuilder(builder)
.WithInterFont()
.UseReactiveUI();

View File

@ -4,6 +4,9 @@
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<WasmMainJSPath>AppBundle\main.js</WasmMainJSPath>
<OutputType>Exe</OutputType>
<WasmAllowUndefinedSymbols>true</WasmAllowUndefinedSymbols>
<MSBuildDebugEngine>1</MSBuildDebugEngine>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
<ItemGroup>

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Notes.Desktop;
class FileService: IFileService
{
public string Combine(string args0, string args1)
{
return Path.Combine(args0, args1);
}
public string GetRandomFileName()
{
return Path.GetRandomFileName();
}
public IEnumerable<string> EnumerateFiles(string path, string patten)
{
return Directory.EnumerateFiles(path,patten);
}
public string GetFileName(string filename)
{
return Path.GetFileName(filename);
}
public string ReadAllText(string filename)
{
return File.ReadAllText(filename);
}
public DateTime GetLastWriteTime(string filename)
{
return File.GetLastWriteTime(filename);
}
public bool ExistsFile(string filename)
{
return File.Exists(filename);
}
public void Delete(string path)
{
File.Delete(path);
}
public void WriteAllText(string path, string text)
{
File.WriteAllText(path,text);
}
}

View File

@ -1,6 +1,7 @@
using System;
using Avalonia;
using Avalonia.ReactiveUI;
using Notes.ViewModels;
namespace Notes.Desktop;
@ -10,8 +11,14 @@ sealed class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static void Main(string[] args)
{
var services = new Services();
services.Register<IFileService>(new FileService());
App.services = services;
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()

View File

@ -8,9 +8,12 @@ namespace Notes;
public partial class App : Application
{
public static IServices services { get; set; }
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
FileSystem.fileService = services.GetSafeAs<IFileService>();
}
public override void OnFrameworkInitializationCompleted()

67
Notes/IFileService.cs Normal file
View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
namespace Notes;
public interface IFileService
{
string Combine(string args0, string args1);
string GetRandomFileName();
IEnumerable<string> EnumerateFiles(string path, string patten);
string GetFileName(string filename);
string ReadAllText(string filename);
DateTime GetLastWriteTime(string filename);
bool ExistsFile(string filename);
void Delete(string path);
void WriteAllText(string path, string text);
}
public static class FileSystem
{
public static IFileService fileService;
public static string Combine(string args0, string args1)
{
return fileService.Combine(args0, args1);
}
public static string GetRandomFileName()
{
return fileService.GetRandomFileName();
}
public static IEnumerable<string> EnumerateFiles(string path, string patten)
{
return fileService.EnumerateFiles(path, patten);
}
public static string GetFileName(string filename)
{
return fileService.GetFileName(filename);
}
public static string ReadAllText(string filename)
{
return fileService.ReadAllText(filename);
}
public static DateTime GetLastWriteTime(string filename)
{
return fileService.GetLastWriteTime(filename);
}
public static bool ExistsFile(string filename)
{
return fileService.ExistsFile(filename);
}
public static void Delete(string filename)
{
fileService.Delete(filename);
}
public static void WriteAllText(string path, string text)
{
fileService.WriteAllText(path, text);
}
}

View File

@ -1,7 +1,6 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Utils;
@ -20,37 +19,37 @@ public class Note
{
get
{
var appDataDirectory = Path.Combine(AppContext.BaseDirectory, "Data");
var appDataDirectory = FileSystem.Combine(AppContext.BaseDirectory, "Data");
IOUtils.EnsureDirectory(appDataDirectory,false);
return appDataDirectory;
}
}
public Note()
{
Filename = $"{Path.GetRandomFileName()}{Extension}";
Filename = $"{FileSystem.GetRandomFileName()}{Extension}";
Date = DateTime.Now;
Text = "";
}
public void Save() =>
File.WriteAllText(Path.Combine(AppDataDirectory, Filename), Text);
FileSystem.WriteAllText(FileSystem.Combine(AppDataDirectory, Filename), Text);
public void Delete() =>
File.Delete(Path.Combine(AppDataDirectory, Filename));
FileSystem.Delete(FileSystem.Combine(AppDataDirectory, Filename));
public static Note Load(string filename)
{
filename = Path.Combine(AppDataDirectory, filename);
filename = FileSystem.Combine(AppDataDirectory, filename);
if (!File.Exists(filename))
throw new FileNotFoundException("Unable to find file on local storage.", filename);
if (!FileSystem.ExistsFile(filename))
throw new InvalidOperationException($"Unable to find file '{filename}' on local storage.");
return
new()
{
Filename = Path.GetFileName(filename),
Text = File.ReadAllText(filename),
Date = File.GetLastWriteTime(filename)
Filename = FileSystem.GetFileName(filename),
Text = FileSystem.ReadAllText(filename),
Date = FileSystem.GetLastWriteTime(filename)
};
}
@ -60,13 +59,13 @@ public class Note
string appDataPath = AppDataDirectory;
// Use Linq extensions to load the *.notes.txt files.
return Directory
return FileSystem
// Select the file names from the directory
.EnumerateFiles(appDataPath, "*.notes.txt")
// Each file name is used to load a note
.Select(filename => Load(Path.GetFileName(filename)))
.Select(filename => Load(FileSystem.GetFileName(filename)))
// With the final collection of notes, order them by date
.OrderByDescending(note => note.Date);

View File

@ -14,7 +14,7 @@ public partial class AboutViewModel:ViewModelBase
[RelayCommand]
public void ShowAllNotes()
{
_services.GetAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
_services.GetSafeAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
}
[Obsolete("Used by designer",true)]

View File

@ -26,7 +26,7 @@ public partial class AllNotesViewModel: ViewModelBase
{
var note = new Note();
note.Filename = string.Empty;
this._services.GetAs<MainViewModel>().Navitation<NoteViewModel>(this._services,this,note);
this._services.GetSafeAs<MainViewModel>().Navitation<NoteViewModel>(this._services,this,note);
}
[RelayCommand]
public void DeleteNote(ISelectionModel selectionModel)
@ -43,7 +43,7 @@ public partial class AllNotesViewModel: ViewModelBase
[RelayCommand]
public void OpenAbout()
{
this._services.GetAs<MainViewModel>().Navitation<AboutViewModel>(this._services);
this._services.GetSafeAs<MainViewModel>().Navitation<AboutViewModel>(this._services);
}
[Obsolete("Used by designer",true)]

View File

@ -0,0 +1,8 @@
namespace Notes.ViewModels;
public interface IServices
{
void Register<T>(T t);
T? Get<T>();
T GetSafeAs<T>();
}

View File

@ -1,40 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using CommunityToolkit.Mvvm.ComponentModel;
using Utils;
namespace Notes.ViewModels;
public interface IServices
{
void Register<T>(T t);
T? Get<T>();
T GetAs<T>();
}
public sealed class Sevices:IServices
{
private readonly Dictionary<Type, object?> map = new Dictionary<Type, object?>();
public void Register<T>(T t)
{
map.Add(typeof(T), t);
}
public T? Get<T>()
{
map.TryGetValue(typeof(T), out var t);
return (T?)t;
}
public T GetAs<T>()
{
T? foo = this.Get<T?>();
if (foo is { } t)
return t;
throw new InvalidOperationException($"Not find type '{typeof(T)}'");
}
}
public partial class MainViewModel:ViewModelBase
{
/// <summary>
@ -47,7 +17,7 @@ public partial class MainViewModel:ViewModelBase
public MainViewModel()
{
_service = new Sevices();
_service = App.services;
_service.Register(this);
_contentViewModel = new AboutViewModel(_service);
}

View File

@ -51,13 +51,13 @@ public partial class NoteViewModel : ViewModelBase
public void Back()
{
_services.GetAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
_services.GetSafeAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
}
[RelayCommand]
public void OpenNote()
{
this._services.GetAs<MainViewModel>().Navitation<NoteViewModel>(this._services, this.AllNotesViewModel, this.Note);
this._services.GetSafeAs<MainViewModel>().Navitation<NoteViewModel>(this._services, this.AllNotesViewModel, this.Note);
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
namespace Notes.ViewModels;
public sealed class Services:IServices
{
private readonly Dictionary<Type, object?> map = new Dictionary<Type, object?>();
public void Register<T>(T t)
{
this.map.Add(typeof(T), t);
}
public T? Get<T>()
{
this.map.TryGetValue(typeof(T), out var t);
return (T?)t;
}
public T GetSafeAs<T>()
{
T? foo = this.Get<T?>();
if (foo is { } t)
return t;
throw new InvalidOperationException($"Not find type '{typeof(T)}'");
}
}

View File

@ -9,7 +9,6 @@ public partial class MainView : UserControl
{
public MainView()
{
DataContext = new MainViewModel();
InitializeComponent();
}
}