Sometimes you'll want a little more control over how your objects are composed than the IServiceCollection API gives you. You can always fall back to explicit registration.
The AddTransient/Scoped/Singleton methods all have an overload that takes a Func as a factory method to use when an instance is needed.
When is this useful? let's say you have the following objects
public interface IPing
{
void Ping(string msg);
}
public class ConsolePing : IPing
{
public void Ping(string msg) =>
Console.WriteLine(msg);
}
public class LogPing : IPing
{
public void Ping(string msg) =>
Log.Info(msg);
}
So you have to different implementations of IPing, then for different services you wanted to inject different implementations, but still only take a dependency on the interface for test mocking.
public class ServiceA
{
private readonly IPing _pinger;
public ServiceA(IPing pinger) => _pinger = pinger;
}
public class ServiceB
{
private readonly IPing _pinger;
public ServiceB(IPing pinger) => _pinger = pinger;
}
How do you register ServiceA and ServiceB so that A gets the ConsolePinger, and B gets the LogPinger? Use the overload registration that takes in a factory method.
The factory method needs to return an instance of the service you are registering, but it takes as a parameter a service provider that can be used to obtain any dependencies.
public void ConfigureService(IServiceCollection services)
{
services.AddTransient<ConsolePing>();
services.AddTransient<LogPing>();
services.AddTransient<ServiceA>(provider =>
{
var consolePinger = provider.GetRequiredService<ConsolePing>();
return new ServiceA(consolePinger);
});
// if you can also omit the generic parameter
services.AddTransient(provider =>
{
var logPinger = provider.GetRequiredService<LogPing>();
return new ServiceB(logPinger);
});
}
Top comments (0)