.Net Core Dependency Injection

Dependency Injection (DI) is software design pattern that decouples (or loosely couples) components in an application. It is based on the Dependency Inversion Principle (DIP) which is part of a greater design principle of Inversion of Control (IOC). The components of an application, whether it be classes or modules, are instantiated at run-time – which is done by abstracting code dependencies with the use of interfaces. These interfaces are what get instantiated at runtime by a container (containing the concrete components) ‘injects’ the concrete classes in place of the abstract interfaces. This strategy has 3 obvious benefits:

  • extensibility – since code is built using abstract interfaces, we can always change the underlying concrete definitions at anytime and easily inject them into the old code
  • testability – similar to extensibility but with abstract interfaces, we’re able to ‘inject’ test or mock components to test the code
  • maintainability – with extensibility and testability we’re able to easier maintainability of the overall code

A container (aka service collection or factory) has the concrete definitions that are to be injected. In prior ASP.NET we often had to use third party tools like Unity to implement this container. In .Net Core, this is builtin to the framework and is called IServiceCollection. It is configured in the Services section of the Startup class.

 

.Net Core

Unlike tradition ASP.NET where we had to use third party libraries to implement dependency injection, .Net Core has DI built into it’s “core”. It is done through the IServiceProvider using constructor injection by default. This is configured in the Startup class in the ConfigureServices method. There we can define the different service containers used by the application. There some out-of-the-box provided services by .Net Core, which are listed below.

Service Type Lifetime
Microsoft.AspNetCore.Hosting.IHostingEnvironment Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.Logging.ILogger<T> Singleton
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Transient
Microsoft.AspNetCore.Http.IHttpContextFactory Transient
Microsoft.Extensions.Options.IOptions<T> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Transient
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<T> Transient
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IApplicationLifetime Singleton

 

Also in the ConfigureServices method of the Startup class we need to register the services that we are using by calling the Add* methods on the IServiceCollection. This not only includes the application services (such as AddMvc or DbContext) but also custom services used by the application.

 

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores()
        .AddDefaultTokenProviders();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}

Note that the bottom section of the example above we are registering two abstract services to a concrete service (IEmailSender and ISmsSender). In this example we are using AddTransient, which defines the service’s lifetime.

 

Service Lifetimes and Registration Options

When registering services for DI, there are different lifetime options. Depending on how the service is to be used by the application, we can register it with one of the following options.

Transient (services.AddTransient<…>)

Transient services are created each time they are requested. These are ideal for lightweight, stateless services. There are two areas of concerns when using transient services. First the obvious one is that since a new instance is created for every request, having a multitude of requests may result in excess of service instances. In such cases it should be considered if a singleton would handle the service better and avoid performance hits. However, on the flip side we have to consider that singletons live throughout the the life of the application and so it could also have performance hits of the requests are hitting different resources as all those resources would be tied to a single instance.

Scoped (services.AddScopted<…>)

Scoped services are once per request. This lifetime is often used for entity framework contexts. This is because the DBContext will get disposed after every request, which makes it act like a transaction. This can also be used for caching services since the cache check/response would be a per request instance.

Singleton (services.AddSingleton<…>)

Singleton services instantiate at startup and remain in scope until the application shuts down. It is similar to static variables or classes used throughout an application. This is useful for services that work for all requests in the application, such as custom ActionResult handlers or request rate handlers.

 

References

Martin Fowler
https://martinfowler.com/articles/injection.html

MSDN Documentation
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

DI Lifetimes Explained
https://dotnetcoretutorials.com/2017/03/25/net-core-dependency-injection-lifetimes-explained/

Stackoverflow Discussion
https://stackoverflow.com/questions/38138100/what-is-the-difference-between-services-addtransient-service-addscope-and-servi

.Net Core Service Lifetimes
https://joonasw.net/view/aspnet-core-di-deep-dive

MSDN Article
https://msdn.microsoft.com/en-us/magazine/mt707534.aspx

dotnetcurry
http://www.dotnetcurry.com/aspnet-core/1426/dependency-injection-di-aspnet-core

MSDN Article (ASP.NET Traditional)
https://msdn.microsoft.com/en-us/library/hh323705(v=vs.100).aspx