ASP .NET Core MVC Dependency Injection Overview
Tags: dotnet, csharp
Published: 2020-02-22 13:32:21

ASP .NET Core MVC comes with dependency injection built in and the options available will cover most use cases. In this post I will summarize what is available in the framework and how to use it.  

I usually did not use dependency injection in my projects in previous versions of the MVC framework. The main reason why is previous versions didn't support dependency injection out of the box and required a 3rd party library like ninject. I thought the projects I worked on would not benefit from it enough to justify another 3rd party library to learn and dependency to keep track of. With ASP .NET Core MVC, that has all changed. Dependency injection is now built in and can be configured directly from the `ConfigureServices` method in the `Startup` class through a series of extension methods. It is also available throughout the framework. This includes custom attributes, filters, view components, etc, or POCOs that handle any custom business logic. This makes dependency management a better fit than in previous versions of the framework.  

There are 3 main variations for adding dependencies: scoped, transient, and singleton. Scoped will use the same instance for a single web request, transient will create a new instance each time a dependency is needed, and singleton creates a single instance for the entire application. Each of these has several overloads.The table below details the common ones.

|Extension Method|Description|
|----------------|-----------|
|`AddScoped<TImplementation>(IServiceCollection)`|Adds a scoped service of type TImplementation to the specified IServiceCollection.|
|`AddScoped<TService,TImplementation>(IServiceCollection)`|Adds a scoped service of type TService with an implementation of type TImplementation to the specified IServiceCollection|
|`AddScoped<TImplementation>(IServiceCollection, Func<IServiceProvider,TService>)`|Adds a scoped service of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|
|`AddScoped<TService,TImplementation>(IServiceCollection, Func<IServiceProvider,TImplementation>)`|Adds a scoped service of type TService with an implementation of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|
|`AddSingleton<TImplementation>(IServiceCollection)`|Adds a singleton service of type TImplementation to the specified IServiceCollection.|
|`AddSingleton<TService,TImplementation>(IServiceCollection)`|Adds a singleton service of type TService with an implementation of type TImplementation to the specified IServiceCollection|
|`AddSingleton<TImplementation>(IServiceCollection, Func<IServiceProvider,TService>)`|Adds a singleton service of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|
|`AddSingleton<TService,TImplementation>(IServiceCollection, Func<IServiceProvider,TImplementation>)`|Adds a singleton service of type TService with an implementation of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|
|`AddTransient<TImplementation>(IServiceCollection)`|Adds a transient service of type TImplementation to the specified IServiceCollection.|
|`AddTransient<TService,TImplementation>(IServiceCollection)`|Adds a transient service of type TService with an implementation of type TImplementation to the specified IServiceCollection|
|`AddTransient<TImplementation>(IServiceCollection, Func<IServiceProvider,TService>)`|Adds a transient service of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|
|`AddTransient<TService,TImplementation>(IServiceCollection, Func<IServiceProvider,TImplementation>)`|Adds a transient service of type TService with an implementation of type TImplementation to the specified IServiceCollection. The instance is provided by the factory specified by the Func delegate argument|

The complete list of ServiceCollection extension methods including the ones related to dependency injection can be found [on the microsoft docs.](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollection?view=aspnetcore-2.0#extension-methods)  

This list covers all common use cases and will meet the requirements for most projects. I have setup a simple project that shows an example of a singleton, a transient, and a scoped dependency. The full source code can be found on [GitHub.](https://github.com/cwl157/asp-net-core-dependency-injection-example)  

The sample application shows a list of books with the ability to add a book. It also keeps track of the total number of pages and tracks each method visited per request.  
![Book List](https://dev-to-uploads.s3.amazonaws.com/i/nl2wle5nsrvo4t90jifg.PNG)  
![Add Book](https://dev-to-uploads.s3.amazonaws.com/i/z0opxzjge2akeb3ftjmy.PNG)  
![Homepage after adding a book](https://dev-to-uploads.s3.amazonaws.com/i/u5uclm8kcdbwseanbgj4.PNG)  
![Total Pages view](https://dev-to-uploads.s3.amazonaws.com/i/gppxgcy8jq9jicawhybr.PNG)  
![Request Tracker](https://dev-to-uploads.s3.amazonaws.com/i/vgrpr2meaxir6pvhlcjn.PNG)  

I am not going to go through every detail of the example application but just highlight the parts that use dependency injection. The list of books is stored in memory so it will persist throughout the life of the application. If the application is restarted, the list resets. For this type of behavior, I inject a singleton. Book storage can also have multiple implementations so I defined an interface for it. `public class InMemoryBookData : IBookData`. The application also features a service named `PagesCounter` that sums the total number of pages in all books. I'm using the term "service" here to mean a generic class containing business logic. This doesn't need to persist state across requests so it is a transient dependency. The third feature tracks each method called per request titled `RequestTrackerData`. Because it needs to track each method in a request, it is a scoped dependency. This allows the same instance to be injected into multiple classes with a fresh instance for each request. As mentioned above, dependency injection is configured in the `Startup.ConfigureServices` method.
```csharp
    public void ConfigureServices(IServiceCollection services)
    {
         services.AddSingleton<IBookData, InMemoryBookData>();
         services.AddScoped<RequestTrackerData>();
         services.AddTransient<ICounter, PagesCounter>();
         services.AddMvc();
    }
```
Dependency injection happens through constructor injection. The `HomeController` needs `IBookData`, `RequestTrackerData`, and `ICounter` for the `PagesCounter` so all 3 are injected and the framework manages what instance is actually passed in.
```csharp
    public HomeController(IBookData b, RequestTrackerData tr, ICounter pc)
    {
         _bda = b;
         _tracker = tr;
         _pageCounter = pc;
    }
```
This is the basic theory of dependency injection regardless of what library or framework is used. However, the ASP .NET Core MVC framework makes managing dependencies better than previous versions. Dependency injection is supported throughout the MVC framework as well. This includes custom attributes, filters, view components, or POCOs that handle any custom business logic. It can all take advantage of the same dependency injection configuration. Browse through the [example on GitHub](https://github.com/cwl157/asp-net-core-dependency-injection-example) to see a complete working project. I now use dependency injection throughout my ASP .NET Core MVC applications and find it easier to implement compared to instantiating instances directly in constructors like I used to do.