diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceBuilder.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceBuilder.cs new file mode 100644 index 0000000..f496758 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceBuilder.cs @@ -0,0 +1,7 @@ +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public class DefaultServiceBuilder(IServiceCollection services, string name) : IServiceBuilder where TService : class +{ + public string Name { get; } = name; + public IServiceCollection Services { get; } = services; +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceFactory.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceFactory.cs new file mode 100644 index 0000000..c60f3e8 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/DefaultServiceFactory.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.Options; + +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public class DefaultServiceFactory( + IServiceProvider serviceProvider, + IOptionsMonitor> optionsMonitor) : IServiceFactory where TService : class +{ + private readonly IServiceProvider _serviceProvider = serviceProvider; + private readonly IOptionsMonitor> _optionsMonitor = optionsMonitor; + + public TService CreateService(string name = "Default") + { + var options = _optionsMonitor.Get(name); + + TService service; + + if (options.CustomFactory != null) + { + // Use custom factory if provided + var args = GatherConstructorArguments(options); + service = options.CustomFactory(_serviceProvider, args); + } + else + { + // Use default creation with ActivatorUtilities + var args = GatherConstructorArguments(options); + service = (TService)ActivatorUtilities.CreateInstance(_serviceProvider, typeof(TService), args); + } + + // Apply registered service actions + foreach (var action in options.ServiceActions) + { + action(service, _serviceProvider); + } + + return service; + } + + private object[] GatherConstructorArguments(ServiceFactoryOptions options) + { + var allArgs = new List(); + + foreach (var argProvider in options.ConstructorArgumentProviders) + { + var args = argProvider(_serviceProvider); + allArgs.AddRange(args); + } + + return allArgs.ToArray(); + } +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceBuilderExtentions.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceBuilderExtentions.cs new file mode 100644 index 0000000..6266360 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceBuilderExtentions.cs @@ -0,0 +1,83 @@ +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public static class GenericServiceBuilderExtensions +{ + public static IServiceBuilder ConfigureService( + this IServiceBuilder builder, + Action> configureOptions) + where TService : class + { + builder.Services.Configure>(builder.Name, configureOptions); + return builder; + } + + public static IServiceBuilder ConfigureService( + this IServiceBuilder builder, + Action, IServiceProvider> configureOptions) + where TService : class + { + builder.Services.Configure>(builder.Name, options => + { + options.ServiceActions.Add((service, serviceProvider) => + { + configureOptions(options, serviceProvider); + }); + }); + return builder; + } + + public static IServiceBuilder WithConstructorArgs( + this IServiceBuilder builder, + params object[] args) + where TService : class + { + return builder.ConfigureService(options => + { + options.ConstructorArgumentProviders.Add(_ => args); + }); + } + + public static IServiceBuilder WithConstructorArgs( + this IServiceBuilder builder, + Func argsProvider) + where TService : class + { + return builder.ConfigureService(options => + { + options.ConstructorArgumentProviders.Add(argsProvider); + }); + } + + public static IServiceBuilder AddServiceAction( + this IServiceBuilder builder, + Action action) + where TService : class + { + return builder.ConfigureService(options => + { + options.ServiceActions.Add(action); + }); + } + + public static IServiceBuilder WithCustomFactory( + this IServiceBuilder builder, + Func customFactory) + where TService : class + { + return builder.ConfigureService(options => + { + options.CustomFactory = customFactory; + }); + } + + public static IServiceBuilder WithCustomFactory( + this IServiceBuilder builder, + Func customFactory) + where TService : class + { + return builder.ConfigureService(options => + { + options.CustomFactory = (serviceProvider, _) => customFactory(serviceProvider); + }); + } +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceCollectionExtentions.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceCollectionExtentions.cs new file mode 100644 index 0000000..cb60c15 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/GenericServiceCollectionExtentions.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public static class GenericServiceCollectionExtensions +{ + public static IServiceBuilder AddServiceWithFactory( + this IServiceCollection services, + string name = "Default") + where TService : class + { + // Register the generic factory + services.TryAddSingleton, DefaultServiceFactory>(); + + // Register named options for this service type + services.Configure>(name, options => { }); + + // Register the service using the factory + services.TryAddTransient(serviceProvider => + { + var factory = serviceProvider.GetRequiredService>(); + return factory.CreateService(name); + }); + + return new DefaultServiceBuilder(services, name); + } + + public static IServiceBuilder AddServiceWithFactory( + this IServiceCollection services, + string name = "Default") + where TService : class + where TImplementation : class, TService + { + // Register the generic factory + services.TryAddSingleton, DefaultServiceFactory>(); + + // Register named options + services.Configure>(name, options => + { + // Set custom factory to create TImplementation + options.CustomFactory = (serviceProvider, args) => + (TService)ActivatorUtilities.CreateInstance(serviceProvider, typeof(TImplementation), args); + }); + + // Register the service interface + services.TryAddTransient(serviceProvider => + { + var factory = serviceProvider.GetRequiredService>(); + return factory.CreateService(name); + }); + + // Also register the concrete type + services.TryAddTransient(serviceProvider => + (TImplementation)serviceProvider.GetRequiredService()); + + return new DefaultServiceBuilder(services, name); + } +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceBuilder.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceBuilder.cs new file mode 100644 index 0000000..d174179 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceBuilder.cs @@ -0,0 +1,7 @@ +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public interface IServiceBuilder where TService : class +{ + public string Name { get; } + public IServiceCollection Services { get; } +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceFactory.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceFactory.cs new file mode 100644 index 0000000..52e700e --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/IServiceFactory.cs @@ -0,0 +1,6 @@ +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public interface IServiceFactory where TService : class +{ + TService CreateService(string name = "Default"); +} diff --git a/src/InteractiveCLI/ServiceConfigurationHelpers/ServiceFactoryOptions.cs b/src/InteractiveCLI/ServiceConfigurationHelpers/ServiceFactoryOptions.cs new file mode 100644 index 0000000..b981004 --- /dev/null +++ b/src/InteractiveCLI/ServiceConfigurationHelpers/ServiceFactoryOptions.cs @@ -0,0 +1,8 @@ +namespace InteractiveCLI.ServiceConfigurationHelpers; + +public class ServiceFactoryOptions where TService : class +{ + public List> ServiceActions { get; set; } = new(); + public IList> ConstructorArgumentProviders { get; private set; } = new List>(); + public Func? CustomFactory { get; set; } +}