Software Development

Dependency Injection in .Net Core

           

What is Dependency?

dependency is an object that another object depends upon.

For example;

public class EmployeeController : ControllerBase
{
IEmployee _employee = new EmployeeImplementor();
//business logic
}

 In the above example, the class EmployeeController directly depends upon class EmployeeImplementor, so EmployeeImplementor is the dependency of class EmployeeController.

What Is Dependency Injection

Dependency Injection is a programming technique that makes a class loosely coupled by making it independent of its dependencies.

Injection dependency simply means that the dependency is pushed into the class from outside.

As per Dependency Inversion Principle a class should not depend upon concretion but rather abstraction. It means that dependencies should not be instantiated using the “new“ operator in a class, instead inject them from outside the class.

We can improve the above code by using dependency injection as follows:

public class EmployeeController : ControllerBase
{                     
private readonly IEmployee _employee;
public EmployeeController(IEmployee employee)
{
             _employee = employee;
      }
            // business logic
  }

In the above class, we have replaced the concrete dependency (EmployeeImplementor) with the abstract one (IEmployee) and it will be injected through the constructor form outside the class.

Why do we need Dependency Injection?

There are many advantages:

  • Flexible Code: If we have multiple implementations for a service, we can switch one implementation with another without changing the business logic.
  • Easy to test: Because we rely on interfaces over implementations -hence, we can more easily test our code.

Built in Dependency Injection in .Net Core

.NET Core provides a built-in Dependency Injection container, IServiceProvider, Which is being used by a lot of internal services like:

  • Hosting Environment
  • Configuration
  • Routing
  • MVC
  • Logging

The container is sometimes referred to as IoC, Inversion of Control Container.

The overall idea is to Register at the application startup and then Resolve at runtime when needed.

Let’s see an example where we will use .Net Core built in container for dependency injection.

For this,

  • Create an ASP.Net Core web application of API type and name it as “CoreDependencyInjection”.
  • Now, Create new folder and name it as Model.
  • Add the following files in the model folder:

public class Employee
            {
                        public  int Id { get; set; }
                        public string Name { get; set; }
                        public string Address { get; set; }
            }

public interface IEmployee
            {
                        List<Employee> GetAllEmployees();
            }

public class EmployeeImplementor :IEmployee
            {
                        public List<Employee> GetAllEmployees()
                        {
                                    List<Employee> employees = new List<Employee>
                                    {
                                            new Employee{Id=1, Name=”Nitin”, Address=”abc”},
                                             new Employee{Id=2,Name=”Amit”, Address=”xyz”}
                                    };
                                    return employees;
                        }
            }

  • Now, we have added the domain class Employee.cs, an interface IEmployee.cs and its implementation EmployeeImplementor
  • Now, under controller folder, add a new controller EmployeeController and add the following code in it:

namespace CoreDependencyInjection.Controllers

{
  [ApiController]
  [Route(“[controller]”)]
  public class EmployeeController : ControllerBase
  {
  // IEmployee _employee = new EmployeeImplementor();// Concrete Dependency
   
private readonly IEmployee _employee;
    public EmployeeController(IEmployee employee)
            {
                  _employee = employee;
            }
            [HttpGet]
            public List<Employee> Get()
            {
                        return _employee.GetAllEmployees();
            }
   }
}

As you can see that the Employee controller does not depend upon any concrete class, rather it’s accepting the dependency through an interface using constructor injection.

  • Now, we will inject the dependency EmployeeImplementor() from our startup class with the following code:

        public void ConfigureServices(IServiceCollection services)
              {
                        services.AddControllers();
                        services.AddSingleton<IEmployee, EmployeeImplementor>();
              }

The above code i.e.  services.AddSingleton<IEmployee, EmployeeImplementor>() is Registering the type IEmployee with the type EmployeeImplementor. At run time, the type will be resolved and the dependency will be injected to the EmployeeController’s constructor.

  • Now we can call the GetAllEmployees() of EmployeeImplementor class.

The benefit of this approach is that if in near future we found a new Implementation of IEmployee, we won’t need to make any change in EmployeeController class. We will just need to update the registration of the type.  

Register Services in Container and Service Lifetime

There are different extension methods in the container to register services depending upon their Lifetime:

1) Singleton: Singleton means only a single instance will ever be created. That instance is shared between all components that require it. The same instance is thus used always.

2) Transient: Transient components are created every time they are requested and are never shared. This means, for example, that a service injected in the constructor of a class will last as long as that class instance exists.

3) Scoped: Scoped means an instance is created once per scope. A scope is created on every request to the application, thus any components registered as Scoped will be created once per request.

Using Third party Ioc Containers

Apart from using the built-in container, .Net Core allows to use the third party containers, which provides more flexibility.

We will be using “Autofac” which is third party IoC Container, in our existing application. The main concept behind inversion of control is to let the caller decide how the dependencies are created, instead of the class deciding it. Therefore, the class never creates an instance, rather expects it to be passed by the caller.

  • So, to begin with, add a reference to the nuget package Autofac.Extensions.DependencyInjection in your project.
  • After this, update the ConfigureServices() in the startup.cs with the following code:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//Create the container builder.
var builder = new ContainerBuilder();
builder.Populate(services);
//Register dependencies
builder.RegisterType<EmployeeImplementor>().As<IEmployee>();
var container = builder.Build();
//return the IServiceProvider implementation
return new AutofacServiceProvider(container);
}

  • You also need to update Program.cs file with the following code:

public class Program
                {
                                public static void Main(string[] args)
                                {
                                                BuildWebHost(args).Run();
                                }
                                public static IWebHost BuildWebHost(string[] args) =>
                                   WebHost.CreateDefaultBuilder(args)
                                                   .UseStartup<Startup>()
                                                   .Build();
                }

Using Middleware in .NET Core

In .NET Core, Middlewares are the components that make up the HTTP pipeline that handles requests and responses for the application. Each piece of middleware called has the option to do some processing on the request before calling the next piece of middleware in line. After execution returns from the call to the next middleware, there is an opportunity to do processing on the response.

Let’s take an example to log the Request and Response in the application using a middleware. For logging, we will be using “Serilog”. We need to install the following Nuget packages:

  1. Serilog
  2. Serilog.Sinks.File
  • After this, we need to add a new class RequestResponseLoggingMiddleware:

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly Serilog.ILogger _logger;
    public RequestResponseLoggingMiddleware(RequestDelegate next, Serilog.ILogger logger)
    {
        _next = next;
        _logger = logger;
    }
    public async Task Invoke(HttpContext context)
    {
        //First, get the incoming request
        var request = await FormatRequest(context.Request);
        _logger.Information(“request: “+request);
        //Copy a pointer to the original response body stream
        var originalBodyStream = context.Response.Body;
        //Create a new memory stream…
        using (var responseBody = new MemoryStream())
        {
            //…and use that for the temporary response body
            context.Response.Body = responseBody;
            //Continue down the Middleware pipeline, eventually returning to this class
            await _next(context);
            //Format the response from the server
             var response = await FormatResponse(context.Response);
            _logger.Information(“response: “+response);
            //Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
            await responseBody.CopyToAsync(originalBodyStream);
        }
    }
    private async Task<string> FormatRequest(HttpRequest request)
    {
        var body = request.Body;
        var buffer = new byte[Convert.ToInt32(request.ContentLength)];
        await request.Body.ReadAsync(buffer, 0, buffer.Length);
        var bodyAsText = Encoding.UTF8.GetString(buffer);
        request.Body = body;
        return $”{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}”;
    }
    private async Task<string> FormatResponse(HttpResponse response)
    {
        response.Body.Seek(0, SeekOrigin.Begin);
        string text = await new StreamReader(response.Body).ReadToEndAsync();
        response.Body.Seek(0, SeekOrigin.Begin);
        return $”{response.StatusCode}: {text}”;
    }
}

  • To enable logging using serilog, We need to update our ConfigureServices() under Startup.cs with the following code:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton((ILogger)new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File(“Log.txt”)
.CreateLogger());
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterType<EmployeeImplementor>().As<IEmployee>();
var container = builder.Build();
return new AutofacServiceProvider(container);
}

  • At last, we need to add the following code:

app.UseMiddleware<RequestResponseLoggingMiddleware>();

in our Configure() method in startup.cs

When you run the application, you will see a Log file in you project’s route folder where you can see the Log for request and response.

It will be something like below:

At Last, we can say that Dependency Injection can make an application even more flexible and Easily testable.

Nitin Sagar

Leave a Reply