Dependency Injection in ASP.NET Core

Views: 2397
Comments: 0
Like/Unlike: 2
Posted On: 04-May-2019 01:45 

Share:   fb twitter linkedin
Rahul M...
4916 Points
27 Posts

Introduction

Dependency Injection (DI) is a software design pattern that is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. According to IoC, the direction of dependency within the application should be in the direction of abstraction, not implementation details. Dependency Injection (DI) makes loosely coupled application that provides greater maintainability, testability and also re-usability. ASP.NET Core has built-in support for dependency injection (DI).


There are three type of Dependency Injection (DI):

  • Construction Injection
    The Construction Injection accepts their dependency at constructor level it means at the time of object creation of the class, their dependency pass through the constructor of the class. It gives the strong dependency contract between objects.
  • Property or Setter Injection
    In this type of dependency injection, dependency inject through public property instead of constructor. It gives us freedom to pass the dependencies when they required. It does not produce strong dependency contract between objects.
  • Interface based Injection
    The interface-based dependency injection can be established by creating the common interface and other classes are implements this interface to inject the dependency. In this type of DI, we can achieve this by either constructor injection or setter injection.


What is Dependency and why use Dependency Injection?

Any object that required by another object is called dependency. Take a look of the following MessageDependency class with a WriteMessage method that other classes in an application depend upon:

public class MessageDependency
{
  public MessageDependency()
  {
  }

  public Task WriteMessage(string message)
  {
    Console.WriteLine(
       $"MessageDependency.WriteMessage is called. Message: {message}");

    return Task.FromResult(0);
  }
}

Now, an instance of the MessageDependency class can be created to bring the WriteMessage method available to a class. The MessageDependency class is a dependency of the CustomModel class:

public class CustomModel : PageModel
{
  MessageDependency _dependency = new MessageDependency();

  public async Task OnGetAsync()
  {
    await _dependency.WriteMessage(
           "CustomModel.OnGetAsync created this message.");
  }
}

In the above example CustomModel class created and directly depends on the MessageDependency instance. In this example code dependencies are problematic and should be avoided for the following reasons:

  1. To replace MessageDependency with a different implementation, the class must be modified.
  2. If MessageDependency has dependencies, they must be configured by the class. In a large project with multiple classes depending on MessageDependency, the configuration code becomes scattered across the application.
  3. This implementation is difficult to unit test. The app should use a mock or stub MessageDependency class, which isn't possible with this approach.


Dependency injection resolve these problems through:

  1. The use of an interface to abstract the dependency implementation.
  2. Registration of the dependency in a service container. ASP.NET Core provides a built-in service container, IServiceProvider. Services are registered in the application's Startup.ConfigureServices method.
  3. Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.


Now, above example code can be modify by using dependency injection as follows:

  1. Create IMessageDependency interface
    public interface IMessageDependency
    {
      Task WriteMessage(string message);
    }​
  2. Imlement IMessageDependency interface to concrete class MessageDependency
    public class MessageDependency : IMessageDependency
    {
      private readonly ILogger<MessageDependency> _logger;
      public MessageDependency(ILogger<MessageDependency> logger)
      {
        _logger = logger;
      }
      public Task WriteMessage(string message)
      {
        _logger.LogInformation(
          "MessageDependency.WriteMessage is called. Message: {MESSAGE}",
          message);
        return Task.FromResult(0);
      }
    }​
  3. IMessageDependency and ILogger<TCategoryName> must be registered in the service container. Now register IMessageDependency in Startup.ConfigureServices. ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      services.AddScoped<IMessageDependency, MessageDependency>();
    }​

As per above examples, there are two type of service container provided by the ASP.net core:

  • Framework Services and
  • Application Services

The framework services are service that are provided by the ASP.net core such as ILoggerFactory etc. The application services are the custom services created base on our requirement as IMessageDependency.

Service lifetime

ASP.Net Core permit us to specify the lifetime for registered services. After the specified life-time the service instance gets disposed automatically. So we don't need to care about the cleaning these dependency, it will be taken care by ASP.net core framework. There are three type of life-times.

  • Transient
    Transient lifetime services are created each time when they are requested from the service container. This lifetime can be used for lightweight, stateless services.
    public void ConfigureServices(IServiceCollection services)
    {
      .....
      services.AddTransient<IMessageDependency, MessageDependency>();
      ....
    }​
  • Scoped
    Scoped lifetime services are created once per client request (connection). It will create a new instance in new request.
    Note: We need to take precausion while, service registered via Scoped in middleware and inject the service in the Invoke or InvokeAsync methods. If we inject dependency via constructor, it behave like singleton object.
    public void ConfigureServices(IServiceCollection services)
    {
      .....
      services.AddScoped<IMessageDependency, MessageDependency>();
      ....
    }​
  • Singleton
    Singleton lifetime services are created only first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Every subsequent request uses the same instance. If the application requires singleton behavior, leave on the service container to manage the service's lifetime is recommended. We don't need to implement the singleton design pattern and provide user code to manage the object's lifetime in the class.
    public void ConfigureServices(IServiceCollection services)
    {
      .....
      services.AddSingleton<IMessageDependency, MessageDependency>();
      ....
    }​

Conclusion
Dependency Injection (DI) provides greater maintainability, testability and also re-usability and ASP.NET Core has built-in support for dependency injection (DI). Dependency Injection help us to decouple our module from their concrete dependencies, improving testability and extensibility of our applications.

Referrences
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2
https://docs.microsoft.com/en-us/dotnet/standard/modern-web-apps-azure-architecture/architectural-principles#dependency-inversion

 

0 Comments
 Log In to Chat