Dependency Injection in ASP.NET Core

Dependency Injection in ASP.NET Core

Dependency Injection (DI)is an ASP.NET Core MVC technique to achieve loosely coupling between objects. The working of the Dependency Injection is like:

If a Controller has a dependency on another class, then in the constructor of the Controller the dependency for the class is defined. On seeing this dependency the Core MVC provides the class object to the Controller automatically. So this provides loosely coupling between the Controller and the other class.

Create the Example Project

For this DI tutorial, use ASP.NET Core Web Application (.NET Core) template to create a new Empty Project and name it ‘DependencyInjection’. Remember to select the framework as .NET Core and version as ASP.NET Core 2.0.

Model & Repository

Create Models folder in the root of the project and add a class called Product with codes shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
 

Next create a new class called IRepository inside the Models folder and define an Interface in it. The code is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public interface IRepository
    {
        IEnumerable<Product> Products { get; }

        Product this[string name] { get; }

        void AddProduct(Product product);

        void DeleteProduct(Product product);
    }
}

This interface will be implemented in another class called Repository. So create Repository.cs inside the Models folder and add the following code to it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class Repository : IRepository
    {
        private Dictionary<string, Product> products;
        public Repository()
        {
            products = new Dictionary<string, Product>();
            new List<Product> {
                new Product { Name = "Women Shoes", Price = 99M },
                new Product { Name = "Skirts", Price = 29.99M },
                new Product { Name = "Pants", Price = 40.5M }
            }.ForEach(p => AddProduct(p));
        }

        public IEnumerable<Product> Products => products.Values;
        public Product this[string name] => products[name];
        public void AddProduct(Product product) => products[product.Name] = product;
        public void DeleteProduct(Product product) => products.Remove(product.Name);
    }
}

Since I don’t want to fetch the products from the database for this tutorial therefore I am creating some products that will be kept in the Memory.

This is done by the constructor of the Repository class that initializes a new dictionary of products and adds 3 products to this dictionary.

Configuration

Go to the Startup.cs class and add the necessary services and configurations as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace DependencyInjection
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStatusCodePages();
            app.UseDeveloperExceptionPage();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }
}

Controller

Create ‘Controllers’ folder in the root of the project and add a new controller called ‘HomeController’ to it. The code for this controller is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View(new Repository().Products);
        }
    }
}

You can see clearly that in the Index Action Method I am creating a new object of the Repository class and calling its Products property (that contains all its products), then finally returning it on the default View as a Model.

My Home Controller is dependent on the Repository class in a Tightly Coupled way. With Dependency Injection technique I will make them Loosely Coupled in the below section.

bower.json

Create bower.json file in the root of the project and add bootstrap package in the dependencies section:

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "bootstrap": "v4.1.3"
  }
}

_ViewImports.cshtml

Add _ViewImports.cshtml file inside the Views folder with the following code:

@using DependencyInjection.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Views

Add Index View inside the Views/Home folder with code given below:

@model IEnumerable<Product>
@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Dependency Injection</title>
    <link rel="stylesheet" asp-href-include="lib/bootstrap/dist/css/*.min.css" />
</head>
<body class="m-1 p-1">
    @if (ViewData.Count > 0)
    {
        <table class="table table-bordered table-sm table-striped">
            @foreach (var kvp in ViewData)
            {
                <tr><td>@kvp.Key</td><td>@kvp.Value</td></tr>
            }
        </table>
    }
    <table class="table table-bordered table-sm table-striped">
        <thead>
            <tr><th>Name</th><th>Price</th></tr>
        </thead>
        <tbody>
            @if (Model == null)
            {
                <tr><td colspan="3" class="text-center">No Model Data</td></tr>
            }
            else
            {
                @foreach (var p in Model)
                {
                    <tr>
                        <td>@p.Name</td>
                        <td>@string.Format("{0:C2}", p.Price)</td>
                    </tr>
                }
            }
        </tbody>
    </table>
</body>
</html>

This View takes a model of type IEnumerable type since the Index action returns all the products in IEnumerable manner. This View will show all the Products in a HTML table.

Run your application and you will see all the 3 products as shown by the image given below:

tightly couple example

Tightly Coupled Controller

If you see the Controllers code you will find it is Tightly Coupled to the Repository class. Since it needs to create the object of Repository class in order to return the products to the View:

return View(new Repository().Products); 

Tightly Coupled components are bad programming practise since they cause:

1. Problems in maintenance of the project – because if one component changes then it will affect the other component too.

2. Problems when Unit Testing the components.
Suppose after some time you need to show some other products that are listed in another class named ‘NewRepository’. In that case you have to make changes to your Controller’s Index action like this:

public IActionResult Index()
{
    return View(new NewRepository().Products);
}

This practise is not a good one.

The best practise would be the Home Controller class should not have any knowledge of which class product class to use or how it is instantiated. And this will be achieved through Dependency Injection (DI).

Implementing Dependency Injection (DI)

ASP.NET Core MVC makes Dependency Injection very easy to apply.

The Repository Class must implement an Interface, which I have already done and in my case is IRepository interface. Now use that Interface instead of the Repository class in your Home Controller.

You have to do 2 steps here:

1. Add a variable of the Interface type (IRepository) in your controller.
2. Add a constructor having a parameter of the interface type. Inside the constructor you set the value of the
interface variable to the value of the parameter.

Now in your action method you can access the products using the interface itself.

These 2 steps are given in the updated Home Controller code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        public HomeController(IRepository repo)
        {
            repository = repo;
        }

        public IActionResult Index()
        {
            return View(repository.Products);
        }
    }
}

Now you have to tell Core MVC how to resolve the dependency of IRepository interface. So go the Startup class and inside the ConfigureServices() method, add the code line that is highlighted below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRepository, Repository>();
    services.AddMvc();
}

The AddTransient method that I used in the listing tells the service provider how to handle a dependency, the first type being the interface (here IRepository) while the second type being the implementation class (here Repository).

Now you rerun your application and can see the products displayed in the View, just like below, but here you used Dependency Injection to lightly-couple the Controller and the Repository class.

The below image illustrates the concept of Dependency Injection:

dependency injection

Advantage of DI

Suppose at a later time you need to show products from NewRepository class. So now you just need to change the AddTransient method to – services.AddTransient();.

You don’t have to do any change in the Home Controller, that is all because the Loosely Coupled Components created from DI.

Congratulations you have successfully used Dependency Injection technique in your project. Now I will introduce you to other aspects of DI.

Resolving Dependency Chains

The meaning of Dependency Chains is – the Dependency itself has a dependency on another component. For example if a Component ‘Alpha’ has a dependency on Component ‘Beta’ while Beta has a dependency on Component ‘Gamma’, then this becomes a Dependency Chain.

Dependency Injection feature of ASP.NET Core MVC is very intelligent to resolve the Dependency Chain. Let me explain you with an example.

Add a class file called ‘IStorage.cs’ to the Models folder and used it to define the interface shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public interface IStorage
    {
        IEnumerable<Product> Items { get; }
        Product this[string key] { get; set; }
        bool ContainsKey(string key);
        void RemoveItem(string key);
    }
}

Now implement this interface in a new class called ‘Storage.cs’ which should be added to the Models folder. The code of the Storage.cs class is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class Storage : IStorage
    {
        private Dictionary<string, Product> items = new Dictionary<string, Product>();
        public Product this[string key]
        {
            get { return items[key]; }
            set { items[key] = value; }
        }
        public IEnumerable<Product> Items => items.Values;
        public bool ContainsKey(string key) => items.ContainsKey(key);
        public void RemoveItem(string key) => items.Remove(key);
    }
}

The Storage class defines the behaviour of a simple storage mechanism for Product objects.

Now go to your previously created Repository.cs class and create a dependency on IStorage interface. For doing this add an argument of IStorage type in the constructor.

Next change all the methods and properties which now work with the IStorage object, like what is shown in the updated code for the Repository class below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class Repository : IRepository
    {
        private IStorage storage;
        public Repository(IStorage repo)
        {
            storage = repo;
            new List<Product> {
                new Product { Name = "Women Shoes", Price = 99M },
                new Product { Name = "Skirts", Price = 29.99M },
                new Product { Name = "Pants", Price = 40.5M }
            }.ForEach(p => AddProduct(p));
        }

        public IEnumerable<Product> Products => storage.Items;

        public Product this[string name] => storage[name];

        public void AddProduct(Product product) => storage[product.Name] = product;

        public void DeleteProduct(Product product) => storage.RemoveItem(product.Name);
    }
}

Now I have created a Dependency Chain as:

1. Home Controller Class depends upon IRepository object.
2. IRepository object in turn depends upon IStorage object.

Now all I have to do is to tell the Service provider how to resolve this Dependency Chain. So go to the Startup.cs class and add the code line (services.AddTransient();) inside the ConfigureServices() method as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRepository, Repository>();
    services.AddTransient<IStorage, Storage>();
    services.AddMvc();
}

Rerun your application and you will see all your products displayed in the browser.

Dependency Injection for Single Type

If you have a simple class that does not implement an Interface then it is a Single Type. In such a case you can use the Dependency Injection technique.

Create a new class called ‘ProductSum.cs’ inside the Models folder and add the below listed code to it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class ProductSum
    {
        public IRepository Repository { get; set; }
        public ProductSum(IRepository repo)
        {
            Repository = repo;
        }
        
        public decimal Total => Repository.Products.Sum(p => p.Price);
    }
}

Note that this class does not implement any interface. It contains a property called ‘Total’ that returns a sum of all the products of the Repository class.

The class has a dependency on IRepository interface and will be resolved by the service provider due to the configuration which I already have applied (in the above section) on the ConfigureServices() method.

Now in your Home Controller, you create a dependency for this ‘ProductSum’ class in the Constructor and set a ViewBag variable that contains the sum of all the products.

The ViewBag variables will be shown in the View:

The updated code for the Home controller is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        private ProductSum productSum;
        public HomeController(IRepository repo, ProductSum psum)
        {
            repository = repo;
            productSum = psum;
        }

        public IActionResult Index()
        {
            ViewBag.Total = productSum.Total;
            return View(repository.Products);
        }
    }
}

Now configure the service provider telling it how to resolve this new dependency. So add the following highlighted code line in the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRepository, Repository>();
    services.AddTransient<IStorage, Storage>();
    services.AddTransient<ProductSum>();
    services.AddMvc();
}

Note that there is no mapping between a service type and an implementation type in this situation. I have used the AddTransient() method with only one parameter. In this way I tell the service provider to initiate the ProductSum class to resolve a dependency on this type.

Run your application and you will see the total price of all the products displayed on the browser:

dependency injection for a class

Dependency Injection Methods

You have already seen the AddTransient() Dependency Injection Method. There are also 2 other methods – AddScoped() and AddSingleton methods.

AddTransient

The AddTransient method creates a new instance of the Implementation type every time it resolves a dependency.

AddScoped

The AddScoped method does not always create a new instance of the implementation class. It reuses instance of the Implementation type for the request arising from same HTTP shares the same object.

AddSingleton

The AddSingleton method creates a new instance of the implementation type for the first request only. It then reuses it for every subsequent request.

There are 3 variations for each of the AddTransient, AddScoped & AddSingleton methods:

a.

<service, implType>()

This variation creates an instance of the implementation type for every dependency. In the section ‘Implementing Dependency Injection (DI)’ I have already used this variation.

b.

<service>()

This variation is used to register a Single Type object. In the section ‘Dependency Injection for Single Type’ I have already used this variation.

c.

<service>(factoryFunc)

This variation is used to register a factory function that will be invoked to create implementation objects. I will implement this variation in the later end of the tutorial.

Using AddTransient Method

The AddTransient() method tells the service provider to create a new instance of the implementation type every time it resolves a dependency.

I have had the AddTransient methods already there in my Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRepository, Repository>();
    services.AddTransient<IStorage, Storage>();
    services.AddTransient<ProductSum>();
    services.AddMvc();
}

For the Repository class there are 2 things to note:

1. The Home Controller has a dependency on the Repository.cs and the ProductSum.cs classes. See the constructor of the Home controller.

2. The ProductSum.cs class has a dependency on the Repository.cs class.

So the service provider will create 2 instances of the Repository.cs as I am using the AddTransient() method. One instance will be created to resolve the dependency of the Home Controller and other to resolve the dependency of ProductSum.cs class.

Now create an override of the ToString() method, that returns a new GUID, in the Repository.cs class as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class Repository : IRepository
    {
        // Removed for clarity
        private string guid = System.Guid.NewGuid().ToString();
        public override string ToString()
        {
            return guid;
        }
    }
}

The GUID will help us identify the specific instance of the Repository.cs class.

Next go to the Home Controller and in the Index Action and adds 2 ViewBag variables. First ViewBag variable will contain the GUID received from the Repository class object while the Second ViewBag variable contains the GUID received from the ProductSum class object.

The code for this is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        // Removed for clarity

        public IActionResult Index()
        {
            ViewBag.HomeControllerGuid = repository.ToString();
            ViewBag.TotalGuid = productSum.Repository.ToString();
            return View(repository.Products);
        }
    }
}

Now run your application and you will see the GUID values from 2 ViewBag variables are different, this specifies that the service provider has created 2 instances of the Repository class.

This image below shows the different values of the GUIDs:

dependency injection through addtransient method

Using AddScoped Method

The AddScoped method does not always create a new instance of the implementation class. It reuses instance of the Implementation type for the request arising from same HTTP shares the same object.

The AddScoped method will tell the service provider to share the single object for all the components that process a request.

Go to the ConfigureServices method of the Startup class and change the AddTransient method to AddScoped for the IRepository object.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IRepository, Repository>();
    services.AddTransient<IStorage, Storage>();
    services.AddTransient<ProductSum>();
    services.AddMvc();
}

Rerun your application and you will see the both GUIDS have the same value. This means the Service Provider has created only one object of the Repository class which is shared between the Home Controller and the ProductSum.cs.

Reload the page and you will see new Guid is generated, because a new HTTP request is initiated and so a new object to the Repository class is created.

The below image illustrates this:

dependency injection through addscoped method

Using AddSingleton Method

The AddSingleton() method tells the Service Provider to ensure only a single object is used for all request for a given type.

Go to ConfigureServices() method and use the AddSingleton() method to resolve the dependency of type IRepository:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IRepository, Repository>();
    services.AddTransient<IStorage, Storage>();
    services.AddTransient<ProductSum>();
    services.AddMvc();
}

Rerun your application and you will see both the GUID values are same, refresh the page but you will notice the GUID values do not changes.

See the image below which illustrates it:

dependency injection through addsingleton method

The AddSingleton method creates a new instance of the Repository class the first time only and it is shared on for every request.

Action Injection

Declaring dependency through a constructor of the Controller can be expensive because the dependency is resolved every time the Controller is created, and also because not all action method need the implementation type object.

In Action Injection dependency is declared through parameters of Action methods. So that only the particular action causes the implementation type to initiate. That means the dependency is not resolved on every time the Controller is called.

Here [FromServices] attribute is applied to the parameters.

Change the Index Action method to include a parameter of ProductSum type and add [FromServices] attribute to it. Also remove the ProductSum attribute from the constructor of the controller.

The updated code is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        public HomeController(IRepository repo)
        {
            repository = repo;
        }

        public IActionResult Index([FromServices] ProductSum productSum)
        {
            ViewBag.HomeControllerGuid = repository.ToString();
            ViewBag.TotalGuid = productSum.Repository.ToString();
            return View(repository.Products);
        }
    }
}

Rerun your application and you will see the ProductSum type will be resolved only when the Index action method is invoked and not when Controller is invoked.

Using a Factory Function (factoryFunc)

This variation is used to register a factory function that will be invoked to create implementation objects. Let us create a small example to understand it.

Create a new class called NewRepository.cs inside the Models folder and add the following code to it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DependencyInjection.Models
{
    public class NewRepository : IRepository
    {
        private Dictionary<string, Product> products;

        public NewRepository()
        {
            products = new Dictionary<string, Product>();
            new List<Product> {
                new Product { Name = "Women Shoes", Price = 99M },
                new Product { Name = "Skirts", Price = 29.99M },
                new Product { Name = "Pants", Price = 40.5M }
            }.ForEach(p => AddProduct(p));
        }

        public IEnumerable<Product> Products => products.Values;
        public Product this[string name] => products[name];
        public void AddProduct(Product product) => products[product.Name] = product;
        public void DeleteProduct(Product product) => products.Remove(product.Name);
    }
}

Now go to the Startup class and add the Factory function to resolve the dependeny of IRepository class. This factory function is added inside the ConfigureServices method and is given below:

services.AddTransient<IRepository>(provider =>
{
    if (env.IsDevelopment())
    {
        var x = provider.GetService<Repository>();
        return x;
    }
    else
    {
        return new NewRepository();
    }
});
services.AddTransient<Repository>();

It checks if the environment is development then create a new instance of Repository class else new instance of NewRepository class is created.

For the ConfigureServices method to access the environment you have to add a constructor, to the Statup class, having parameter of type IHostingEnvironment.

The updated code for the Startup.cs is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DependencyInjection.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace DependencyInjection
{
    public class Startup
    {
        private IHostingEnvironment env;
        public Startup(IHostingEnvironment hostEnv) => env = hostEnv;

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IRepository>(provider =>
            {
                if (env.IsDevelopment())
                {
                    var x = provider.GetService<Repository>();
                    return x;
                }
                else
                {
                    return new NewRepository();
                }
            });
            services.AddTransient<Repository>();
            services.AddTransient<IStorage, Storage>();
            services.AddTransient<ProductSum>();
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStatusCodePages();
            app.UseDeveloperExceptionPage();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }
}

You can download the full codes of this tutorial from the below link:

Download

Conclusion

In this tutorial I explained working with Dependency Injection feature which is a new addition in the Core MVC framework. I hope you find it useful.

Share this article -

yogihosting

ABOUT THE AUTHOR

This article has been written by the Technical Staff of YogiHosting. Check out other articles on "ASP.NET Core, jQuery, EF Core, SEO, jQuery, HTML" and more.