Dependency Injection Conditional Registration in ASP.NET Core

Now that I know emailing and SMS are working I wanted a way to get the information from the out going messages without actually hitting a third party service or having to use break points. To do this I wrote a new class that implemented IEmailSender and ISmsSender that wrote the messages to a file instead of hitting Mailgun and Twilio.

Dependency Injection (DI)

ASP.NET Core comes with dependency injection built-in the details of which are described in the docs and the code can be found here. The docs article does a great job of explaining DI so I am going to keep my description limited to the example at hand.

In ASP.NET Core services are registered in the ConfigureServices of the StartUp class. When an application first starts running the StartUp constructor runs followed by ConfigureServices and finally Configure.

Configuration Based Services

One potential way to handle decisions on which services to register is via configuration settings.  Using the following configuration as an example if WriteToFile is true then the file writer version of the email service should be used instead of the version that actually emails users.

{
  "EmailSettings": {
    "WriteToFile": true,
    "Path": "C:\Email\"
  }
}

With this configuration in place ConfigureServices uses the following to load the proper service based on the config value.

if (Configuration.Get<bool>("EmailSettings:WriteToFile"))
{
    services.AddTransient<IEmailSender, AuthMessageSenderFileWriter>();
}
else
{
    services.AddTransient<IEmailSender, AuthMessageSender>();
}

Environment Based Services

ASP.NET Core’s IHostingEnvironment defines an EnvironmentName which automatically gets loaded by the host from an environment variable. To quote Visual Studio’s Object Browser on EnvironmentName:

Gets or sets the name of the environment. This property is automatically set by the host to the value of the “Hosting:Environment” (on Windows) or “Hosting__Environment” (on Linux & OS X) environment variable.

I found this post by Armen Shimoon which clued me in on the fact that ConfigureServices is only called ASP.NET Core if no Configure{EnvironmentName}Services is found. This means that if EnviromentName is set to Development then ConfigureDevelopmentServices will be called instead of ConfigureServices. Extensions are provided out of the box to help support Development, Staging and Production values.

For development runs of the application to write to a file instead of emailing I added a ConfigureDevelopmentServices.

public void ConfigureDevelopmentServices(IServiceCollection services)
{
    ConfigureServices(services);
    services.AddTransient<IEmailSender, AuthMessageSenderFileWriter>();
}

The first thing this function does is to call ConfigureServices since that is where the majority of service registrations is done. ConfigureServices registers an IEmailSender with AuthMessageSender, but that registration is overwritten with AuthMessageSenderFileWriter in ConfigureDevelopmentServices after the ConfigureServices function returns.

Final Thoughts

I have presented a couple of ways to handled varying registrations with ASP.NET Core’s built in dependency injection. I see using the ability to run different version of ConfigureServices to be the one I will get the most use out of, but I am sure basing the decision off of configuration could come in handy as well.

If you know of other options please leave a comment.

Dependency Injection Conditional Registration in ASP.NET Core Read More »