TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

ASP.NET Core : Overview Of Dependency Injection

Introduction

A software developer writes a lot of code that is tightly coupled; and when complexity grows, the code will eventually deteriorate into spaghetti code; in other words, the application design being a bad design.

Dependency Injection (DI) is a pattern where objects are not responsible for creating their own dependencies. Dependency Injection is a way to remove hard-coded dependencies among objects, making it easier to replace an object's dependencies, either for testing (using mock objects in unit test) or to change run-time behavior.

Before understanding Dependency Injection, we should be familiar with the two concepts of Object Oriented Programming - tight coupling and loose coupling. Let's see each, one by one.

Tight Coupling

When a class is dependent on a concrete dependency, it is said to be tightly coupled to that class. A tightly coupled object is dependent on another object; that means changing one object in a tightly coupled application often requires changes to a number of other objects. It is not difficult when an application is small but in an enterprise level application, it is too difficult to make the changes.

Loose Coupling

It means two objects are independent and an object can use another object without being dependent on it. It is a design goal that seeks to reduce the inter- dependencies among components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.

Now in short, Dependency Injection is a pattern that makes objects loosely coupled instead of tightly coupled. When we are designed classes with DI, they are more loosely coupled because they do not have direct, hard-coded dependencies on their collaborators. This follows the Dependency Inversion Principle(DIP).

There are three types of dependency injections,

  1. Constructor Dependency Injection
  2. Setter Dependency Injection
  3. Interface Dependency Injection

Dependency Inversion Principle (DIP)

It is the fifth principle of SOLID where “D” stands for Dependency Inversion Principle. Its main goal is decoupling software modules, in other words software design should be loosely coupled instead of tightly coupled. The principle states:

  1. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.

In short, the higher-level module defines an interface and lower-level module implements that interface. To explain this sentence we use a real-life example.

Suppose you are sitting on your desk. Your desk has some gadgets, like your development machine (LCD Monitor or Laptop) and mobile phone. The LCD Monitor has a cable that connects from the electric port (power cable) and the same as the mobile phone that also has a charging cable that also connects to an electric port. You could see that both devices connect from the electric port so the question occurs of who defined the port, your device or the cable? You will say that the devices define the port, in other words we don't purchase devices depending on port while the port is designed dependent on devices and the cable is just an interface that connects both devices and the port so you could say that a high-level module doesn't depend on the low-level module but both should be dependent on abstraction.


Inversion of Control (IoC) Pattern

In pratice, an application is deigned with many classes. So when we use DI with these classes then these classes are requesting their dependecies via their constructor that’s why it's helpful to have a class dedicated to creating these classes with their associated dependencies. These classes are referred to as containers, or more specifically, Inversion of Control (IoC) containers or Dependency Injection (DI) containers.

The Inversion of Control (IoC) containers is a factory that is reponsible for providing instance of types that are requested from it. If a given type has declared that it has dependencies, and the container has been configured to provide the dependency types, it will create the dependencies as part of creating the requested instance. Suppose an application designed with the Strategy design pattern. There are designed interfaces and implemented with classes. The IoC conatiner configured to such way that when application is requesting for interface then it provides that interface depedecies i.e class instance, hence class object can be provided to classes without the need for any hard-coded object construction.


ASP.NET Core and DI

The ASP.NET Core itself provides basic built in IoC container that is represented by IserviceProvider interface. It supports constructor depedency injection by default. ASP.NET Core uses DI for for instantiating all its components and services. The container is configured in ConfigureService method of the startup.cs class as this class is entry point to application. We configure the bulit-in and custome services and components that can be used in the entire application life cycle.

The ASP.NET Core services can be configured with the three lifetimes and registration options.

Figure 1: Service lifetime options

Let’s discuss the difference between these lifetime and registration options one by one.

Transient

The AddTransient method is used for the Transient lifetime option. This AddTransient method is used to register services in IoC container and it get instantiated each time when it is accessed. If we have a service which is used multiple places in same request, a new instance would be created each time.

Suppose we have a user service which is used to access user detail and user name so we create two method for these operations. One method is used in action method and another is used on view so there are two new instance create as we requeste for both methods of user service from different part controller and view.

As this method get instantiated on each request that’s why it should be used for lightweight and stateless services.

We create an interface named IUniqueKeyProviderService which has a method signature as per following code snippet.

namespace DIApplication.Service 
{ 
    public interface IUniqueKeyProviderService 
    { 
        int GetUniqueKey(); 
    } 
}

Now, we create a class named UniqueKeyProviderService which has implementation of IUniqueKeyProviderService interface per following code snippet.

namespace DIApplication.Service 
{ 
    public class UniqueKeyProviderService: IUniqueKeyProviderService 
    { 
        public int GetUniqueKey() 
        { 
            return GetHashCode(); 
        } 
    } 
}

Now, we register this IUniqueKeyProviderService service with its depedencies using AddTransient option in the ConfigureServices method of the Startup class as per following code snippet.

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

Now, we create HomeController to consume method of the IUniqueKeyProviderService service. The following code snippet is for the same.

using DIApplication.Service; 
using Microsoft.AspNetCore.Mvc; 
   
namespace DIApplication.Controllers 
{ 
    public class HomeController : Controller 
    { 
        private readonly IUniqueKeyProviderService uniqueKeyProviderService; 
   
        public HomeController(IUniqueKeyProviderService uniqueKeyProviderService) 
        { 
            this.uniqueKeyProviderService = uniqueKeyProviderService; 
        } 
        
        public IActionResult Index() 
        { 
            int model = uniqueKeyProviderService.GetUniqueKey();             
            return View(model); 
        }         
    } 
}

As per the above code, the controller used constructor dependency injection to inject IUniqueKeyProviderService service. We can also inject IUniqueKeyProviderService service on view to access any method of service. We show unique id on view so create index view as per following code snippet.

@model int 
@inject DIApplication.Service.IUniqueKeyProviderService uniqueKeyProviderService; 
<h2>Transient LifeTime</h2
<h4>Unique key from service instance created in controller: @Model</h4
<h4>Unique key from service instance created in view: @uniqueKeyProviderService.GetUniqueKey()</h4>

Now it runs the application and shows result as follows.

Figure 2: Transient Lifetime

As the result in the above figure shows that a unique key is different from both requests which means each request creates new instance of IUniqueKeyProviderService.

Scoped

The AddScoped method is used for the Scoped lifetime option. This Add Scoped method is used to register services in IoC container. It ptovides service instance once per request. If we have a service which is used multiple places in the same request, only single instance would be created for that.

We have the same view, controller and service. We register this IUniqueKeyProviderService service with its depedencies using AddScoped option in the ConfigureServices method of the Startup class as per following code snippet.

public void ConfigureServices(IServiceCollection services) 
        {             
            services.AddMvc(); 
            services.AddScoped<IUniqueKeyProviderService, UniqueKeyProviderService>(); 
        }

Now run the application and make two subsequent requests and results shown in the below figure.

Figure 3: Scoped Lifetime

As per the above figure, the AddScoped creates a single instance once per request. When we make another request, then it creates another instance of the service.

Singleton

The AddSingleton method is used for the Singleton lifetime option. This AddSingleton method is used to register services in IoC container. It ptovides service instance the first time it is requested. After that every subsequent request will use the same instance. If we have a service which is used multiple places in the same or separate requests, only single instance wll be created for that.

We have same view, controller and service. We register this IUniqueKeyProviderService service with its depedencies using AddSingleton option in the ConfigureServices method of the Startup class as per following code snippet.

public void ConfigureServices(IServiceCollection services) 
        {             
            services.AddMvc(); 
            services.AddSingleton<IUniqueKeyProviderService, UniqueKeyProviderService>(); 
        }

Now, run the application and make two subsequent requests. The result is shown below.

Figure 4: Singleton Lifetime option

As per the above figure, the AddSignleton creates a single instance when requested the frst time. When we make two subsequent requests, then it uses same instance of the service in both requests.

If an application requires singleton behavior, allowing the services container to manage the service's lifetime is recommended instead of implementing the singleton design pattern and managing class object's lifetime in the class itself.


Framework-Provided Services

The ConfigureServices method in the Startup class is responsible for defining the services the application will use, including platform features like Entity Framework Core, ASP.NET Core MVC and ASP.NET Core Identity. The features and middleware provided by ASP.NET, such as MVC, follow a convention of using a single AddServiceName extension method to register all of the services required by that feature such as AddDbContext, AddIdentity, AddMVC and AddAuthorization etc.

  • AddDbContext :This method is used to add application DbContext to the dependency injection.
  • AddIdentity :The identity services are added to the application in the ConfigureServices method.

 

LINK: https://social.technet.microsoft.com/wiki/contents/articles/37218.asp-net-core-overview-of-dependency-injection.aspx

Web API Architecture And Dependency Injection Best Practices


This article explains the Web API best practices for architecture and Dependency Injection, using Unity and other options available for Dependency Injection.

From last few years, Web API is becoming very popular and these days, a lot of projects are going on with Web API. Thousands of projects have been developed using Web API.

If someone is working on Web API, then its architecture and best practices are the most important things, which enable the developer to create one of the best applications. There are more than a hundred rules and recommendations for Web API best practices but in this article, I am going to explain only about the architecture and Dependency Injection.

Web API Architecture

Hence, before discussing about Dependency Injection, it would be better to discuss Web API architecture best practices because Dependency Injection is based on the architecture. Here, I am going to refer to one of my previous articles.

It is recommended to go through my previous article because I will be using source code and concepts of my previous article. You can download the source from the attachment section of my previous article.

Step 1

Create Layered Architecture

Generally, Web API has 3 layers, which are given below.

  1. Web API Layer
  2. Business Layer (Business Logic Layer)
  3. Repository Layer (Data Access Layer)

You can see that Business Layer interfaces do not have any dependency on repository layer interfaces. Thus, the repository layer interface will be referenced only in the classes of business layer.

Step 2

Add POCO (Plain Old CLR Object).

Step 3

Add repository layer project to implement Repository layer interfaces and Business layer project to implement Business layer interfaces.

In the Solution Explorer, it will look, as shown below.

Now, I am going to implement Dependency Injection, using Unity. Right click on API project and open NuGet Package Manager and search for unity.

Install the two packages, as shown in the preceding screenshot.

After installing Unity at Web API layer, it will add some files and codes, which are shown below.

UnityConfig.cs 

 
  1. public static class UnityConfig {  
  2.     public static void RegisterComponents() {  
  3.         var container = new UnityContainer();  
  4.         GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);  
  5.     }  
  6. }   

UnityResolver.cs

 

 
  1. public class UnityResolver: IDependencyResolver  
  2. {  
  3.     protected IUnityContainer container;  
  4.     public UnityResolver(IUnityContainer container) {…………………………}  
  5.     public IDependencyScope BeginScope() {…………………………}  
  6.     public void Dispose() {…………………………}  
  7.     public object GetService(Type serviceType) {…………………………}  
  8.     public IEnumerable < object > GetServices(Type serviceType) {…………………………}  
  9. }  

Add the code given below to the file.

WebApiConfig.cs

 

 
  1. public static void Register(HttpConfiguration config)  
  2. {  
  3.     var container = new UnityContainer();  
  4.     container.RegisterType < IUserRepository, UserRepository > (new HierarchicalLifetimeManager());  
  5.     container.RegisterType < IUserBusiness, UserBusiness > (new HierarchicalLifetimeManager());  
  6.     container.RegisterType < IBaseRepository < User > , BaseRepository < User >> (new HierarchicalLifetimeManager());  
  7.     config.DependencyResolver = new UnityResolver(container);  
  8. }  

I have seen many people add unity at 2 layers i.e. at Business layer and API layer. Write the codes given below.

API Layer

 

 
  1. container.RegisterType<IUserBusiness, UserBusiness>(new HierarchicalLifetimeManager());  

Business Layer

 

 
  1. container.RegisterType<IUserRepository, UserRepository>(new HierarchicalLifetimeManager());container.RegisterType<IBaseRepository<User>, BaseRepository<User>>(new HierarchicalLifetimeManager());  

I do not endorse the preceding approach due to multiple reasons. Now, I am going to compare pros and cons of 2 approaches.

Approach 1

API Layer

 

 
  1. container.RegisterType<IUserBusiness, UserBusiness>(new HierarchicalLifetimeManager());container.RegisterType<IUserRepository, UserRepository>(new HierarchicalLifetimeManager());container.RegisterType<IBaseRepository<User>, BaseRepository<User>>(new HierarchicalLifetimeManager());  

Approach 2

API Layer

 

 
  1. container.RegisterType<IUserBusiness, UserBusiness>(new HierarchicalLifetimeManager());  

Business Layer

 

 
  1. container.RegisterType<IUserRepository, UserRepository>(new HierarchicalLifetimeManager());container.RegisterType<IBaseRepository<User>, BaseRepository<User>>(new HierarchicalLifetimeManager());  

The screenshot given below explains why the first approach is a better approach,

Injecting Dependencies without any third-party framework

It is not necessary to use any other framework for injecting the dependencies. We can inject dependencies without Unity or any other third party framework.

Go to the class startup.cs and inside the method “ConfigureServices(IServiceCollection services)”, add 2 lines of code given below. 

 
  1. services.AddTransient<IUserManager, UserManager>();  
  2. services.AddTransient<IUserRepository, UserRepository>();   

You can also inject those dependencies from web.config file or from appsettings.json files.

LINK: http://www.c-sharpcorner.com/article/web-api-architecture-and-dependency-injection-best-practices/