From 92ea28d64784f1cbdbdd2ebffa2ea3a81817711e Mon Sep 17 00:00:00 2001 From: Dan Clark Date: Tue, 11 Feb 2025 19:45:02 +0000 Subject: [PATCH] Improve scoped service --- .../{FileProvider.cs => FilePicker.cs} | 6 +-- .../{FolderProvider.cs => FolderPicker.cs} | 6 +-- .../IServiceCollectionExtensions.cs | 16 +++---- Toolkit.Foundation/DefaultHostBuilder.cs | 6 ++- Toolkit.Foundation/FileDescriptor.cs | 4 -- Toolkit.Foundation/FileFilter.cs | 3 -- Toolkit.Foundation/FilePickerFilter.cs | 3 ++ Toolkit.Foundation/FileProvider.cs | 27 ++++++++++++ Toolkit.Foundation/FileProviderFilter.cs | 3 ++ Toolkit.Foundation/FolderFilter.cs | 3 -- Toolkit.Foundation/FolderPickerPicker.cs | 3 ++ Toolkit.Foundation/IFileDescriptor.cs | 10 ----- Toolkit.Foundation/IFilePicker.cs | 6 +++ Toolkit.Foundation/IFileProvider.cs | 10 +++-- Toolkit.Foundation/IFolderPicker.cs | 6 +++ Toolkit.Foundation/IFolderProvider.cs | 6 --- ...opeFactory.cs => IScopedServiceFactory.cs} | 2 +- ...eProvider.cs => IScopedServiceProvider.cs} | 2 +- .../IServiceCollectionExtensions.cs | 44 +++++++++++-------- Toolkit.Foundation/ScopedServiceFactory.cs | 35 +++++++++++++++ ...peProvider.cs => ScopedServiceProvider.cs} | 4 +- Toolkit.Foundation/SelectFilesHandler.cs | 10 ++--- Toolkit.Foundation/SelectFoldersHandler.cs | 10 ++--- Toolkit.Foundation/ServiceScopeFactory.cs | 31 ------------- Toolkit.WinUI/IServiceCollectionExtensions.cs | 4 +- 25 files changed, 149 insertions(+), 111 deletions(-) rename Toolkit.Avalonia/{FileProvider.cs => FilePicker.cs} (82%) rename Toolkit.Avalonia/{FolderProvider.cs => FolderPicker.cs} (75%) delete mode 100644 Toolkit.Foundation/FileDescriptor.cs delete mode 100644 Toolkit.Foundation/FileFilter.cs create mode 100644 Toolkit.Foundation/FilePickerFilter.cs create mode 100644 Toolkit.Foundation/FileProvider.cs create mode 100644 Toolkit.Foundation/FileProviderFilter.cs delete mode 100644 Toolkit.Foundation/FolderFilter.cs create mode 100644 Toolkit.Foundation/FolderPickerPicker.cs delete mode 100644 Toolkit.Foundation/IFileDescriptor.cs create mode 100644 Toolkit.Foundation/IFilePicker.cs create mode 100644 Toolkit.Foundation/IFolderPicker.cs delete mode 100644 Toolkit.Foundation/IFolderProvider.cs rename Toolkit.Foundation/{IServiceScopeFactory.cs => IScopedServiceFactory.cs} (75%) rename Toolkit.Foundation/{IServiceScopeProvider.cs => IScopedServiceProvider.cs} (75%) create mode 100644 Toolkit.Foundation/ScopedServiceFactory.cs rename Toolkit.Foundation/{ServiceScopeProvider.cs => ScopedServiceProvider.cs} (76%) delete mode 100644 Toolkit.Foundation/ServiceScopeFactory.cs diff --git a/Toolkit.Avalonia/FileProvider.cs b/Toolkit.Avalonia/FilePicker.cs similarity index 82% rename from Toolkit.Avalonia/FileProvider.cs rename to Toolkit.Avalonia/FilePicker.cs index a3be506..cfe2b48 100644 --- a/Toolkit.Avalonia/FileProvider.cs +++ b/Toolkit.Avalonia/FilePicker.cs @@ -4,10 +4,10 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class FileProvider(ITopLevelProvider topLevelProvider) : - IFileProvider +public class FilePicker(ITopLevelProvider topLevelProvider) : + IFilePicker { - public async Task> SelectFiles(FileFilter filter) + public async Task> Get(FilePickerFilter filter) { if (topLevelProvider.Get() is TopLevel topLevel) { diff --git a/Toolkit.Avalonia/FolderProvider.cs b/Toolkit.Avalonia/FolderPicker.cs similarity index 75% rename from Toolkit.Avalonia/FolderProvider.cs rename to Toolkit.Avalonia/FolderPicker.cs index 0f6b234..9c1eb38 100644 --- a/Toolkit.Avalonia/FolderProvider.cs +++ b/Toolkit.Avalonia/FolderPicker.cs @@ -4,10 +4,10 @@ using Toolkit.Foundation; namespace Toolkit.Avalonia; -public class FolderProvider(ITopLevelProvider topLevelProvider) : - IFolderProvider +public class FolderPicker(ITopLevelProvider topLevelProvider) : + IFolderPicker { - public async Task> SelectFolders(FolderFilter filter) + public async Task> Get(FolderPickerPicker filter) { if (topLevelProvider.Get() is TopLevel topLevel) { diff --git a/Toolkit.Avalonia/IServiceCollectionExtensions.cs b/Toolkit.Avalonia/IServiceCollectionExtensions.cs index e9b329a..3463523 100644 --- a/Toolkit.Avalonia/IServiceCollectionExtensions.cs +++ b/Toolkit.Avalonia/IServiceCollectionExtensions.cs @@ -12,8 +12,8 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddAvalonia(this IServiceCollection services) { services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); @@ -27,8 +27,8 @@ public static class IServiceCollectionExtensions services.AddTransient(); services.AddAsyncHandler>, WriteClipboardHandler>(); - services.AddAsyncHandler, IReadOnlyCollection?, SelectFoldersHandler>(); - services.AddAsyncHandler, IReadOnlyCollection?, SelectFilesHandler>(); + services.AddAsyncHandler, IReadOnlyCollection?, SelectFoldersHandler>(); + services.AddAsyncHandler, IReadOnlyCollection?, SelectFilesHandler>(); services.AddHandler(nameof(IClassicDesktopStyleApplicationLifetime)); services.AddHandler(nameof(ISingleViewApplicationLifetime)); @@ -51,8 +51,8 @@ public static class IServiceCollectionExtensions new ProxyServiceCollection(services => { services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); @@ -66,8 +66,8 @@ public static class IServiceCollectionExtensions services.AddTransient(); services.AddAsyncHandler>, WriteClipboardHandler>(); - services.AddAsyncHandler, IReadOnlyCollection?, SelectFoldersHandler>(); - services.AddAsyncHandler, IReadOnlyCollection?, SelectFilesHandler>(); + services.AddAsyncHandler, IReadOnlyCollection?, SelectFoldersHandler>(); + services.AddAsyncHandler, IReadOnlyCollection?, SelectFilesHandler>(); services.AddHandler(nameof(ContentControl)); diff --git a/Toolkit.Foundation/DefaultHostBuilder.cs b/Toolkit.Foundation/DefaultHostBuilder.cs index e339eef..89f27e9 100644 --- a/Toolkit.Foundation/DefaultHostBuilder.cs +++ b/Toolkit.Foundation/DefaultHostBuilder.cs @@ -52,8 +52,10 @@ public class DefaultHostBuilder : services.AddTransient(); services.AddTransient(); - services.AddHandlerScoped(); - services.AddHandlerScoped(); + services.AddScopedHandler(); + services.AddScopedHandler(); + + services.AddTransient(); services.AddInitialization(); }); diff --git a/Toolkit.Foundation/FileDescriptor.cs b/Toolkit.Foundation/FileDescriptor.cs deleted file mode 100644 index 3255195..0000000 --- a/Toolkit.Foundation/FileDescriptor.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Toolkit.Foundation; - -public record FileDescriptor(string Name, string Path, long Size) : - IFileDescriptor; \ No newline at end of file diff --git a/Toolkit.Foundation/FileFilter.cs b/Toolkit.Foundation/FileFilter.cs deleted file mode 100644 index e37c437..0000000 --- a/Toolkit.Foundation/FileFilter.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public record FileFilter(string Name, List Extensions, bool AllowMultiple = false); diff --git a/Toolkit.Foundation/FilePickerFilter.cs b/Toolkit.Foundation/FilePickerFilter.cs new file mode 100644 index 0000000..acc4aed --- /dev/null +++ b/Toolkit.Foundation/FilePickerFilter.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record FilePickerFilter(string Name, List Extensions, bool AllowMultiple = false); diff --git a/Toolkit.Foundation/FileProvider.cs b/Toolkit.Foundation/FileProvider.cs new file mode 100644 index 0000000..e834832 --- /dev/null +++ b/Toolkit.Foundation/FileProvider.cs @@ -0,0 +1,27 @@ +namespace Toolkit.Foundation; + +public class FileProvider : + IFileProvider +{ + public IReadOnlyCollection Get(string path, + FileProviderFilter filter) + { + if (!Directory.Exists(path)) + { + return []; + } + + List searchPatterns = filter.Extensions.Count > 0 + ? filter.Extensions.Select(ext => $"*.{ext}").ToList() + : ["*.*"]; + + List files = []; + + foreach (string pattern in searchPatterns) + { + files.AddRange(Directory.EnumerateFiles(path, pattern, SearchOption.TopDirectoryOnly)); + } + + return files; + } +} diff --git a/Toolkit.Foundation/FileProviderFilter.cs b/Toolkit.Foundation/FileProviderFilter.cs new file mode 100644 index 0000000..4dfbf05 --- /dev/null +++ b/Toolkit.Foundation/FileProviderFilter.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record FileProviderFilter(List Extensions); diff --git a/Toolkit.Foundation/FolderFilter.cs b/Toolkit.Foundation/FolderFilter.cs deleted file mode 100644 index 7ff2c9a..0000000 --- a/Toolkit.Foundation/FolderFilter.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public record FolderFilter(bool AllowMultiple = false); \ No newline at end of file diff --git a/Toolkit.Foundation/FolderPickerPicker.cs b/Toolkit.Foundation/FolderPickerPicker.cs new file mode 100644 index 0000000..c801c5a --- /dev/null +++ b/Toolkit.Foundation/FolderPickerPicker.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record FolderPickerPicker(bool AllowMultiple = false); \ No newline at end of file diff --git a/Toolkit.Foundation/IFileDescriptor.cs b/Toolkit.Foundation/IFileDescriptor.cs deleted file mode 100644 index 25ef2b2..0000000 --- a/Toolkit.Foundation/IFileDescriptor.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IFileDescriptor -{ - string Name { get; } - - string Path { get; } - - long Size { get; } -} diff --git a/Toolkit.Foundation/IFilePicker.cs b/Toolkit.Foundation/IFilePicker.cs new file mode 100644 index 0000000..cbf647a --- /dev/null +++ b/Toolkit.Foundation/IFilePicker.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IFilePicker +{ + Task> Get(FilePickerFilter filter); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IFileProvider.cs b/Toolkit.Foundation/IFileProvider.cs index bf29d76..5018cbe 100644 --- a/Toolkit.Foundation/IFileProvider.cs +++ b/Toolkit.Foundation/IFileProvider.cs @@ -1,6 +1,8 @@ -namespace Toolkit.Foundation; - -public interface IFileProvider + +namespace Toolkit.Foundation { - Task> SelectFiles(FileFilter filter); + public interface IFileProvider + { + IReadOnlyCollection Get(string path, FileProviderFilter filter); + } } \ No newline at end of file diff --git a/Toolkit.Foundation/IFolderPicker.cs b/Toolkit.Foundation/IFolderPicker.cs new file mode 100644 index 0000000..e90ed33 --- /dev/null +++ b/Toolkit.Foundation/IFolderPicker.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IFolderPicker +{ + Task> Get(FolderPickerPicker filter); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IFolderProvider.cs b/Toolkit.Foundation/IFolderProvider.cs deleted file mode 100644 index ce91067..0000000 --- a/Toolkit.Foundation/IFolderProvider.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IFolderProvider -{ - Task> SelectFolders(FolderFilter filter); -} \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceScopeFactory.cs b/Toolkit.Foundation/IScopedServiceFactory.cs similarity index 75% rename from Toolkit.Foundation/IServiceScopeFactory.cs rename to Toolkit.Foundation/IScopedServiceFactory.cs index daf7284..a2b4ef8 100644 --- a/Toolkit.Foundation/IServiceScopeFactory.cs +++ b/Toolkit.Foundation/IScopedServiceFactory.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public interface IServiceScopeFactory +public interface IScopedServiceFactory { (IServiceScope, TService) Create(params object?[] parameters); } \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceScopeProvider.cs b/Toolkit.Foundation/IScopedServiceProvider.cs similarity index 75% rename from Toolkit.Foundation/IServiceScopeProvider.cs rename to Toolkit.Foundation/IScopedServiceProvider.cs index 46dff06..1e25f01 100644 --- a/Toolkit.Foundation/IServiceScopeProvider.cs +++ b/Toolkit.Foundation/IScopedServiceProvider.cs @@ -2,7 +2,7 @@ namespace Toolkit.Foundation; -public interface IServiceScopeProvider +public interface IScopedServiceProvider { bool TryGet(TService service, out IServiceScope? serviceScope); } \ No newline at end of file diff --git a/Toolkit.Foundation/IServiceCollectionExtensions.cs b/Toolkit.Foundation/IServiceCollectionExtensions.cs index eb878b0..1a3b567 100644 --- a/Toolkit.Foundation/IServiceCollectionExtensions.cs +++ b/Toolkit.Foundation/IServiceCollectionExtensions.cs @@ -216,11 +216,11 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddHandlerScoped(this IServiceCollection services, + public static IServiceCollection AddScopedHandler(this IServiceCollection services, string key) where THandler : class, IHandler - where TMessage : class => AddHandlerScoped(services, ServiceLifetime.Transient, key); + where TMessage : class => AddScopedHandler(services, ServiceLifetime.Transient, key); - public static IServiceCollection AddHandlerScoped(this IServiceCollection services, + public static IServiceCollection AddScopedHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient, string? key = null) where THandler : class, IHandler where TMessage : class @@ -243,11 +243,11 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddHandlerScoped(this IServiceCollection services, + public static IServiceCollection AddScopedHandler(this IServiceCollection services, string key) where THandler : class, IHandler - where TMessage : class => AddHandlerScoped(services, ServiceLifetime.Transient, key); + where TMessage : class => AddScopedHandler(services, ServiceLifetime.Transient, key); - public static IServiceCollection AddHandlerScoped(this IServiceCollection services, + public static IServiceCollection AddScopedHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient, string? key = null) where THandler : class, IHandler where TMessage : class @@ -351,30 +351,38 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddServiceScope(this IServiceCollection services) - where TServiceScope : notnull + public static IServiceCollection AddScopedService(this IServiceCollection services) + where TScopedService : class { - services.AddCache(); - services.AddTransient, ServiceScopeProvider>(); - services.AddTransient, ServiceScopeFactory>(); + services.AddScoped, ScopedServiceDescriptor>(); + services.AddScoped(provider => provider.GetRequiredService>().Value!); + + services.AddCache(); + + services.AddTransient, ScopedServiceProvider>(); + services.AddTransient, ScopedServiceFactory>(); return services; } - public static IServiceCollection AddServiceScope(this IServiceCollection services, + public static IServiceCollection AddScopedService(this IServiceCollection services, Action providerDelegate) - where TServiceScope : notnull + where TScopedService : class { - services.AddCache(); - services.AddTransient, ServiceScopeProvider>(); - services.AddTransient, ServiceScopeFactory>(provider => + services.AddScoped, ScopedServiceDescriptor>(); + services.AddScoped(provider => provider.GetRequiredService>().Value!); + + services.AddCache(); + + services.AddTransient, ScopedServiceProvider>(); + services.AddTransient, ScopedServiceFactory>(provider => { providerDelegate.Invoke(provider); IServiceScopeFactory factory = provider.GetRequiredService(); - ICache cache = provider.GetRequiredService>(); + ICache cache = provider.GetRequiredService>(); - return new ServiceScopeFactory(factory, cache); + return new ScopedServiceFactory(factory, cache); }); return services; diff --git a/Toolkit.Foundation/ScopedServiceFactory.cs b/Toolkit.Foundation/ScopedServiceFactory.cs new file mode 100644 index 0000000..8a90d4b --- /dev/null +++ b/Toolkit.Foundation/ScopedServiceFactory.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Toolkit.Foundation; + +public class ScopedServiceFactory(IServiceScopeFactory serviceScopeFactory, + ICache cache) : + IScopedServiceFactory + where TScopedService : notnull +{ + public (IServiceScope, TScopedService) Create(params object?[] parameters) + { + if (serviceScopeFactory.CreateScope() is IServiceScope serviceScope) + { + IServiceProvider serviceProvider = serviceScope.ServiceProvider; + + if (serviceProvider.GetRequiredService() is IServiceFactory factory) + { + if (factory.Create(parameters) is TScopedService service) + { + serviceProvider.GetRequiredService>().Set(service); + cache.Add(service, serviceScope); + + foreach (IInitializationScoped initializationScoped in serviceScope.ServiceProvider.GetServices()) + { + initializationScoped.Initialize(); + } + + return (serviceScope, service); + } + } + } + + return default; + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/ServiceScopeProvider.cs b/Toolkit.Foundation/ScopedServiceProvider.cs similarity index 76% rename from Toolkit.Foundation/ServiceScopeProvider.cs rename to Toolkit.Foundation/ScopedServiceProvider.cs index 639db09..445cd78 100644 --- a/Toolkit.Foundation/ServiceScopeProvider.cs +++ b/Toolkit.Foundation/ScopedServiceProvider.cs @@ -2,8 +2,8 @@ namespace Toolkit.Foundation; -public class ServiceScopeProvider(ICache cache) : - IServiceScopeProvider +public class ScopedServiceProvider(ICache cache) : + IScopedServiceProvider where TService : notnull { public bool TryGet(TService service, diff --git a/Toolkit.Foundation/SelectFilesHandler.cs b/Toolkit.Foundation/SelectFilesHandler.cs index 5aa1db5..b0c05df 100644 --- a/Toolkit.Foundation/SelectFilesHandler.cs +++ b/Toolkit.Foundation/SelectFilesHandler.cs @@ -1,14 +1,14 @@ namespace Toolkit.Foundation; -public class SelectFilesHandler(IFileProvider fileProvider) : - IAsyncHandler, IReadOnlyCollection?> +public class SelectFilesHandler(IFilePicker fileProvider) : + IAsyncHandler, IReadOnlyCollection?> { - public async Task?> Handle(SelectionEventArgs args, + public async Task?> Handle(SelectionEventArgs args, CancellationToken cancellationToken) { - if (args.Value is FileFilter filter) + if (args.Value is FilePickerFilter filter) { - if (await fileProvider.SelectFiles(filter) + if (await fileProvider.Get(filter) is { Count: > 0 } files) { return files; diff --git a/Toolkit.Foundation/SelectFoldersHandler.cs b/Toolkit.Foundation/SelectFoldersHandler.cs index ee5ad09..68f9a1b 100644 --- a/Toolkit.Foundation/SelectFoldersHandler.cs +++ b/Toolkit.Foundation/SelectFoldersHandler.cs @@ -1,15 +1,15 @@ namespace Toolkit.Foundation; -public class SelectFoldersHandler(IFolderProvider folderProvider) : - IAsyncHandler, IReadOnlyCollection?> +public class SelectFoldersHandler(IFolderPicker folderProvider) : + IAsyncHandler, IReadOnlyCollection?> { - public async Task?> Handle(SelectionEventArgs args, + public async Task?> Handle(SelectionEventArgs args, CancellationToken cancellationToken = default) { - if (args.Value is FolderFilter filter) + if (args.Value is FolderPickerPicker filter) { - if (await folderProvider.SelectFolders(filter) + if (await folderProvider.Get(filter) is { Count: > 0 } folders) { return folders; diff --git a/Toolkit.Foundation/ServiceScopeFactory.cs b/Toolkit.Foundation/ServiceScopeFactory.cs deleted file mode 100644 index 92ac97b..0000000 --- a/Toolkit.Foundation/ServiceScopeFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Toolkit.Foundation; - -public class ServiceScopeFactory(IServiceScopeFactory serviceScopeFactory, - ICache cache) : - IServiceScopeFactory - where TServiceScope : notnull -{ - public (IServiceScope, TServiceScope) Create(params object?[] parameters) - { - if (serviceScopeFactory.CreateScope() is IServiceScope serviceScope) - { - if (serviceScope.ServiceProvider.GetService() is IServiceFactory factory) - { - if (factory.Create(parameters) is TServiceScope service) - { - cache.Add(service, serviceScope); - foreach (IInitializationScoped initializationScoped in serviceScope.ServiceProvider.GetServices()) - { - initializationScoped.Initialize(); - } - - return (serviceScope, service); - } - } - } - - return default; - } -} \ No newline at end of file diff --git a/Toolkit.WinUI/IServiceCollectionExtensions.cs b/Toolkit.WinUI/IServiceCollectionExtensions.cs index 3b46e62..0d1fcc2 100644 --- a/Toolkit.WinUI/IServiceCollectionExtensions.cs +++ b/Toolkit.WinUI/IServiceCollectionExtensions.cs @@ -16,8 +16,8 @@ public static class IServiceCollectionExtensions services.AddTransient(); services.AddTransient(); - services.AddHandlerScoped(nameof(ContentControl)); - services.AddHandlerScoped(nameof(ContentDialog)); + services.AddScopedHandler(nameof(ContentControl)); + services.AddScopedHandler(nameof(ContentDialog)); services.AddTransient((Func>)(provider => new ProxyServiceCollection(services =>