Explicit Registration in ASP.NET Core DI
Sam Ferree

Sam Ferree @sam_ferree

About: Software Architect Specializing in the C# and ASP.NET Core But I've been lucky enough to dabble in Ruby, Python, Node.js, JAVA, C/C++, and more.

Location:
Midwest America.
Joined:
Feb 14, 2018

Explicit Registration in ASP.NET Core DI

Publish Date: Aug 10 '20
6 0

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
  });
}
Enter fullscreen mode Exit fullscreen mode

Comments 0 total

    Add comment