Dependency Injection In .NET : Usage Of Factory
In my two previous articles, I have covered default .net core/.net5/.net6 IoC container and in second article tried to cover Autofac as IoC container.
Dependency Injection .NET core
Dependency Injection .NET With Autofac
In this article, I will try to cover usage of factory in Dependency Injection.
Just like previous two article, I am going to take same example.
public interface IService
{
string GetMessage();
}
public class ServiceA : IService
{
public string GetMessage() => "Message from ServiceA.";
}
public class ServiceB : IService
{
public string GetMessage() => "Message from ServiceB.";
}
Scenario
- In this scenario, requirement is that need to get
IService
based on text like "ServiceA" or "ServiceB". You can consider this as a named/keyed resolution. This will be helpful in scenario like if you have two database one for customer1 and another for customer 2 then you will pass customer1/customer2 and based on that database context get created.
Solution 1
- Create inline factory using
Func
.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ServiceA>();
builder.Services.AddScoped<ServiceB>();
builder.Services.AddSingleton<Func<IServiceProvider, string, IService>>((sp, name) =>
{
return name switch
{
"ServiceA" => sp.GetRequiredService<ServiceA>(),
"ServiceB" => sp.GetRequiredService<ServiceB>(),
_ => sp.GetRequiredService<ServiceA>()
};
});
builder.Services.AddSingleton<IServiceFactory,SimpleServiceFactory>();
var app = builder.Build();
- Here following is the inline registration of factory.
builder.Services.AddSingleton<Func<IServiceProvider, string, IService>>((sp, name) =>
{
return name.ToUpper() switch
{
"SERVICEA" => sp.GetRequiredService<ServiceA>(),
"SERVICEB" => sp.GetRequiredService<ServiceB>(),
_ => sp.GetRequiredService<ServiceA>()
};
});
- Its usage is like following.
app.MapGet("/hello/{context}", (Func<IServiceProvider, string, IService> service,
IServiceProvider sp,string context) =>
{
return service(sp, context.ToUpperInvariant()).GetMessage();
}).WithName("Hello");
- Run the application and open the url localhost:5000/hello/servicea
output:
Message from ServiceA.
- Run the application and open the url localhost:5000/hello/serviceb
output:
Message from ServiceB.
Solution 2
- There is alternative way to create factory. This is more traditional way using Interface and its implementation.
public interface IServiceFactory
{
IService GetService(string name);
}
public class SimpleServiceFactory : IServiceFactory
{
private readonly IServiceProvider serviceProvider;
public SimpleServiceFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public IService GetService(string name)
{
using var scope = serviceProvider.CreateScope();
return name.ToUpper() switch
{
"SERVICEA" => scope.ServiceProvider.GetRequiredService<ServiceA>(),
"SERVICEB" => scope.ServiceProvider.GetRequiredService<ServiceB>(),
_ => scope.ServiceProvider.GetRequiredService<ServiceA>()
};
}
}
- Register factory.
builder.Services.AddSingleton<IServiceFactory,SimpleServiceFactory>();
- Usage of factory.
app.MapGet("/hellofactory/{context}", (IServiceFactory factory,string context) =>
{
return factory.GetService(context).GetMessage();
}).WithName("HelloFactory");
- Run the application and open the url localhost:5000/hellofactory/servicea ```
output: Message from ServiceA.
- Run the application and open the url http://localhost:5000/hellofactory/serviceb
output: Message from ServiceB.
Hope this helps. There are many internal factory out there and one of IDbContextFactory and you can create your own. Also this helps to resolve service based on context information.