Skip to content

Commit 13b5f8a

Browse files
bogdanstc-freemarketDevJonny
authored andcommitted
Add generic factory registration for services
1 parent 19ef380 commit 13b5f8a

7 files changed

Lines changed: 221 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace InteractiveCLI.ServiceConfigurationHelpers;
2+
3+
public class DefaultServiceBuilder<TService>(IServiceCollection services, string name) : IServiceBuilder<TService> where TService : class
4+
{
5+
public string Name { get; } = name;
6+
public IServiceCollection Services { get; } = services;
7+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Microsoft.Extensions.Options;
2+
3+
namespace InteractiveCLI.ServiceConfigurationHelpers;
4+
5+
public class DefaultServiceFactory<TService>(
6+
IServiceProvider serviceProvider,
7+
IOptionsMonitor<ServiceFactoryOptions<TService>> optionsMonitor) : IServiceFactory<TService> where TService : class
8+
{
9+
private readonly IServiceProvider _serviceProvider = serviceProvider;
10+
private readonly IOptionsMonitor<ServiceFactoryOptions<TService>> _optionsMonitor = optionsMonitor;
11+
12+
public TService CreateService(string name = "Default")
13+
{
14+
var options = _optionsMonitor.Get(name);
15+
16+
TService service;
17+
18+
if (options.CustomFactory != null)
19+
{
20+
// Use custom factory if provided
21+
var args = GatherConstructorArguments(options);
22+
service = options.CustomFactory(_serviceProvider, args);
23+
}
24+
else
25+
{
26+
// Use default creation with ActivatorUtilities
27+
var args = GatherConstructorArguments(options);
28+
service = (TService)ActivatorUtilities.CreateInstance(_serviceProvider, typeof(TService), args);
29+
}
30+
31+
// Apply registered service actions
32+
foreach (var action in options.ServiceActions)
33+
{
34+
action(service, _serviceProvider);
35+
}
36+
37+
return service;
38+
}
39+
40+
private object[] GatherConstructorArguments(ServiceFactoryOptions<TService> options)
41+
{
42+
var allArgs = new List<object>();
43+
44+
foreach (var argProvider in options.ConstructorArgumentProviders)
45+
{
46+
var args = argProvider(_serviceProvider);
47+
allArgs.AddRange(args);
48+
}
49+
50+
return allArgs.ToArray();
51+
}
52+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
namespace InteractiveCLI.ServiceConfigurationHelpers;
2+
3+
public static class GenericServiceBuilderExtentions
4+
{
5+
public static IServiceBuilder<TService> ConfigureService<TService>(
6+
this IServiceBuilder<TService> builder,
7+
Action<ServiceFactoryOptions<TService>> configureOptions)
8+
where TService : class
9+
{
10+
builder.Services.Configure<ServiceFactoryOptions<TService>>(builder.Name, configureOptions);
11+
return builder;
12+
}
13+
14+
public static IServiceBuilder<TService> ConfigureService<TService>(
15+
this IServiceBuilder<TService> builder,
16+
Action<ServiceFactoryOptions<TService>, IServiceProvider> configureOptions)
17+
where TService : class
18+
{
19+
builder.Services.Configure<ServiceFactoryOptions<TService>>(builder.Name, options =>
20+
{
21+
options.ServiceActions.Add((service, serviceProvider) =>
22+
{
23+
configureOptions(options, serviceProvider);
24+
});
25+
});
26+
return builder;
27+
}
28+
29+
public static IServiceBuilder<TService> WithConstructorArgs<TService>(
30+
this IServiceBuilder<TService> builder,
31+
params object[] args)
32+
where TService : class
33+
{
34+
return builder.ConfigureService(options =>
35+
{
36+
options.ConstructorArgumentProviders.Add(_ => args);
37+
});
38+
}
39+
40+
public static IServiceBuilder<TService> WithConstructorArgs<TService>(
41+
this IServiceBuilder<TService> builder,
42+
Func<IServiceProvider, object[]> argsProvider)
43+
where TService : class
44+
{
45+
return builder.ConfigureService(options =>
46+
{
47+
options.ConstructorArgumentProviders.Add(argsProvider);
48+
});
49+
}
50+
51+
public static IServiceBuilder<TService> AddServiceAction<TService>(
52+
this IServiceBuilder<TService> builder,
53+
Action<TService, IServiceProvider> action)
54+
where TService : class
55+
{
56+
return builder.ConfigureService(options =>
57+
{
58+
options.ServiceActions.Add(action);
59+
});
60+
}
61+
62+
public static IServiceBuilder<TService> WithCustomFactory<TService>(
63+
this IServiceBuilder<TService> builder,
64+
Func<IServiceProvider, object[], TService> customFactory)
65+
where TService : class
66+
{
67+
return builder.ConfigureService(options =>
68+
{
69+
options.CustomFactory = customFactory;
70+
});
71+
}
72+
73+
public static IServiceBuilder<TService> WithCustomFactory<TService>(
74+
this IServiceBuilder<TService> builder,
75+
Func<IServiceProvider, TService> customFactory)
76+
where TService : class
77+
{
78+
return builder.ConfigureService(options =>
79+
{
80+
options.CustomFactory = (serviceProvider, _) => customFactory(serviceProvider);
81+
});
82+
}
83+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.Extensions.DependencyInjection.Extensions;
2+
3+
namespace InteractiveCLI.ServiceConfigurationHelpers;
4+
5+
public static class GenericServiceCollectionExtentions
6+
{
7+
public static IServiceBuilder<TService> AddServiceWithFactory<TService>(
8+
this IServiceCollection services,
9+
string name = "Default")
10+
where TService : class
11+
{
12+
// Register the generic factory
13+
services.TryAddSingleton<IServiceFactory<TService>, DefaultServiceFactory<TService>>();
14+
15+
// Register named options for this service type
16+
services.Configure<ServiceFactoryOptions<TService>>(name, options => { });
17+
18+
// Register the service using the factory
19+
services.TryAddTransient<TService>(serviceProvider =>
20+
{
21+
var factory = serviceProvider.GetRequiredService<IServiceFactory<TService>>();
22+
return factory.CreateService(name);
23+
});
24+
25+
return new DefaultServiceBuilder<TService>(services, name);
26+
}
27+
28+
public static IServiceBuilder<TService> AddServiceWithFactory<TService, TImplementation>(
29+
this IServiceCollection services,
30+
string name = "Default")
31+
where TService : class
32+
where TImplementation : class, TService
33+
{
34+
// Register the generic factory
35+
services.TryAddSingleton<IServiceFactory<TService>, DefaultServiceFactory<TService>>();
36+
37+
// Register named options
38+
services.Configure<ServiceFactoryOptions<TService>>(name, options =>
39+
{
40+
// Set custom factory to create TImplementation
41+
options.CustomFactory = (serviceProvider, args) =>
42+
(TService)ActivatorUtilities.CreateInstance(serviceProvider, typeof(TImplementation), args);
43+
});
44+
45+
// Register the service interface
46+
services.TryAddTransient<TService>(serviceProvider =>
47+
{
48+
var factory = serviceProvider.GetRequiredService<IServiceFactory<TService>>();
49+
return factory.CreateService(name);
50+
});
51+
52+
// Also register the concrete type
53+
services.TryAddTransient<TImplementation>(serviceProvider =>
54+
(TImplementation)serviceProvider.GetRequiredService<TService>());
55+
56+
return new DefaultServiceBuilder<TService>(services, name);
57+
}
58+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace InteractiveCLI.ServiceConfigurationHelpers;
2+
3+
public interface IServiceBuilder<TService> where TService : class
4+
{
5+
public string Name { get; }
6+
public IServiceCollection Services { get; }
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace InteractiveCLI.ServiceConfigurationHelpers;
2+
3+
public interface IServiceFactory<TService> where TService : class
4+
{
5+
TService CreateService(string name = "Default");
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace InteractiveCLI.ServiceConfigurationHelpers;
2+
3+
public class ServiceFactoryOptions<TService> where TService : class
4+
{
5+
public List<Action<TService, IServiceProvider>> ServiceActions { get; set; } = new();
6+
public List<Func<IServiceProvider, object[]>> ConstructorArgumentProviders { get; set; } = new();
7+
public Func<IServiceProvider, object[], TService>? CustomFactory { get; set; }
8+
}

0 commit comments

Comments
 (0)