Select Page

Dependency Injection in ASP.NET Core 3.1

ASP.NET Core is designed from the ground up to support the dependency injection design pattern. Dependency injection in ASP.NET Core provides a technique to create applications that are loosely coupled. ASP.NET Core allows us to injects dependent objects either through constructor or methods. Based on the environment in which application is running different objects can be injected.

What is Dependency Injection?

Dependency Injection in ASP.NET Core

Dependency injection is used to achieve loose coupling in application between client objects and their dependencies. Instead of directly instantiating objects of classes and using them rather Interfaces are used and objects are provided to class in some way either through constructor or through some method.

Class will depend on an interface rather than concrete implementation of dependencies. This makes our code loosely coupled as we can inject any concrete object as far as it implements the required interface.

ASP.NET Core comes with a built-in container (IServiceProvider) that supports constructor injection by default and the framework itself provides some services through dependency injection.

Here is a great article by Martin Fowler on Dependency Injection

Why Dependency Injection

  • Improves code readability by keeping it clean so easier to maintain it.
  • Application is loosely coupled which improves flexibility
  • Increases code testing ability with different mock implementations of services.
  • Supports extendable class structure

Implementation of Dependency Injection in ASP.NET Core

There are two types of services in ASP.NET Core

  1. Framework provided services – These services are part of ASP.NET Core framework
  2. Custom Services – These are add-on services created by developers

Registering framework provided services

Below code shows how to register a framework defined service (Memory Caching Service) in ConfigureServices() method of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();
    }
    //Remaining code was removed
}

Registering your own services

You can even register your own service and make it available for dependency injection into controllers, middlewares & views

First you need to define the service using an interface as shown below

public interface ILogger
{
    void Log(string message);
    void Log(string message, Exception ex);
}

public class LogToFile : ILogger
{
    public void Log(string message)
    {
        //Log Message to File
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to File
    }
}

public class LogToDB : ILogger
{
    public void Log(string message)
    {
        //Log Message to Database
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to Database
    }
}

Service Lifetime

Before we talk about how to register your service in the startup.cs file we need to understand the significance of Service Lifetime while registering a service. Lifetime decides whether the instance of a service being injected is a new instance to that component or it same signal instance which is being used everywhere in the application. While registering service you need to choose the lifetime for each service.

Transient – Each time a new object is created for dependency injection i.e. each component will have its unique instance. This is the option used for lightweight & stateless services.

services.AddTransient<ILogger, LogToFile>();

Scoped – This means that it is created once for each request i.e. a new object will be created for each new request.

services.AddScoped<ILogger, LogToFile>();

Singleton – One single component will be created for the lifetime of the application. This single instance is shared across all components and all requests.

services.AddSingleton<ILogger, LogToFile>();

Registering Services in startup.cs

You need to register required services in ConfigureServices() methif of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();

        services.AddTransient<ILogger, LogToDB>();
    }
     //Remaining code has been removed
}

Once service is registered it can be injected into other components

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Injecting services (framework/custom) into Middleware

Below is the code sample for the injection of service into middleware. Either it can be injected through the constructor and or through the invoke method.

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public ExceptionMiddleware(RequestDelegate next, ILogger logger, IMemoryCache cache)
    {
        _next = next;
        _logger = logger;
        _cache = cache;
    }

    public Task Invoke(HttpContext httpContext, ILogger logger, IMemoryCache cache)
    {
        return _next(httpContext);
    }
}

Injecting services (framework/custom) into Controller

Below is the code sample for the injection of service into the controller. Either it can be injected through the constructor and or through the action method.

public class HomeController : Controller
{
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public HomeController(ILogger logger, IMemoryCache cache)
    {
        _logger = logger;
        _cache = cache;
    }

    public IActionResult Index([FromServices] ILogger logger, [FromServices] IMemoryCache cache)
    {
        return View();
    }
}

Request Services Manually

Instead of using dependency injection to inject services, components can even manually request services. Below code shows how to manually request a service using HttpContext object.

var services = this.HttpContext.RequestServices;
var log = (ILogger)services.GetService(typeof(ILogger));

Summary

Dependency injection allows us to develop loosely coupled applications. Dependency injection in ASP.NET Core is supported and also it is simple and easy to implement. Framework provided services or your own custom services can be used for dependency injection.

You can inject components using dependency injection into middlewares, controllers, view, etc. Service lifetime can be used to control the scope of the service.

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

You can also check my another Article on Microsoft Azure Storage – https://procodeguide.com/programming/azure-storage/

OOP Concept – Polymorphism in C# .NET

Polymorphism is one of the important building blocks in object-oriented programming. Poly means many and morph means forms i.e. many forms of an object. It means design by interface i.e. many classes implementing the same interface to provide the same services but each class has its own implementation of those services.

Polymorphism

Polymorphism occurs when a parent class reference is used to refer to an instance of a child class object. An object is said to polymorphic when it passes more than one IS-A test. As in C#, Object class is base class of the classes so any object at any given time will be polymorphic as it passes the IS-A test for own type and as well as to object type.

The class can be accessed through its instance set to a reference variable. A reference variable is always of one single type which cannot be changed. A reference variable can be set to an instance of declared type i.e class or any other class derived from this class.

Types of Polymorphism

There are two type of polymorphism in c# .NET

  1. Static/Compile time
  2. Dynamic/Runtime

Static/compile time is achieved by method overloading or operator overloading & dynamic/Runtime is achieved by method overriding.

Function Overloading – It is a feature in c# .NET where two or more functions can have the same name but different parameters. Due to the different sets of parameters, each function has a different signature.

Operator Overloading – It is a feature in c# .NET allows the same operator to do various operations i.e. it allows to provide new definitions to the given operator.

Function Overriding – It is a feature in c# .NET which allows a child class to have a function that is already present in the base class. The child class can have its own implementation of the overridden function.

Polymorphism – c# .NET example

The below code shows an example of function overloading. There is a logger class with multiple implementations of Log function either to log an error message or exception object or both. Based on arguments passed by calling code appropriate implementation of the function will be called.

    class Logger
    {
        public void Log(string message)
        {
            //Write Message To Log
            return;
        }
        public void Log(Exception ex)
        {
            //Write Exception To Log
            return;
        }

        public void Log(string message, Exception ex)
        {
            //Write Message & Exception To Log
            return;
        }
    }

The below code shows an example of operator overloading. + operator is overloaded to add two class of type room and return details like to total length and breadth.

    class Room
    {
        private double length;
        private double breadth;

        public double Length 
        {
            get { return length; } 
            set { length = value; } 
        }
        public double Breadth
        {
            get { return breadth; }
            set { breadth = value; }
        }

        public double GetArea()
        {
            return (length * breadth);
        }

        public static Room operator+ (Room room1, Room room2)
        {
            Room room = new Room();
            room.Length = room1.Length + room2.Length;
            room.Breadth = room1.Breadth + room2.Breadth;

            return room;
        }
    }

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

The below code shows an example of dynamic polymorphism. The base class is MotorVehicle & child classes MotorCycle & MotorCar are derived from MotorVehicle. The base class has virtual function GetNumberOfWheels & this virtual keyword will force child classes to override this function and provide their own implementation.

Here reference variable motorVehicle is of type MotorVehicle but it can be set to an instance of all 3 classes MotoVehicle, MotorCycle & MotorCar as it passes the IS-A test for all 3 classes. When the same reference variable is set to different instance then the result of function GetNumberOfWheels is also different i.e. implementation of set instance is called.

    class MotorVehicle
    {
        public virtual string GetNumberOfWheels()
        {
            return "Motor Vehicle has two or more wheels";
        }
    }

    class MotorCycle : MotorVehicle
    {
        public override string GetNumberOfWheels()
        {
            return "Motor Cycle has two wheels";
        }
    }

    class MotorCar : MotorVehicle
    {
        public override string GetNumberOfWheels()
        {
            return "Motor Car has four wheels";
        }
    }

//Client Code

MotorVehicle motorVehicle = new MotorVehicle();
MessageBox.Show(motorVehicle.GetNumberOfWheels()); //Motor Vehicle has two or more wheels
motorVehicle = new MotorCycle();
MessageBox.Show(motorVehicle.GetNumberOfWheels()); //Motor Cycle has two wheels
motorVehicle = new MotorCar();
MessageBox.Show(motorVehicle.GetNumberOfWheels()); //Motor Car has four wheels

The below code shows implementation of dynamic polymorphism by using Interface.

    interface MotorVehicle
    {
        string GetNumberOfWheels();
    }

    class MotorCycle : MotorVehicle
    {
        public string GetNumberOfWheels()
        {
            return "Motor Cycle has two wheels";
        }
    }

    class MotorCar : MotorVehicle
    {
        public string GetNumberOfWheels()
        {
            return "Motor Car has four wheels";
        }
    }

//Client Code

MotorVehicle motorVehicle = null;
motorVehicle = new MotorCycle();
MessageBox.Show(motorVehicle.GetNumberOfWheels()); //Motor Cycle has two wheels
motorVehicle = new MotorCar();
MessageBox.Show(motorVehicle.GetNumberOfWheels()); //Motor Car has four wheels

Summary

Polymorphism is a fundamental block of object-oriented programming & it helps to process objects of various types through a single interface.

Static/compile time achieved by method/operator overloading and dynamic/Runtime achieved by method overriding are two types of polymorphism supported by c# .NET

In c# all objects are polymorphic as all classes have default bases class object.

Reference: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism

You can also check my another Article explaining OOP Concept Encapsulation –https://procodeguide.com/programming/oop-concept-encapsulation-in-c-net/

OOP Concept – Encapsulation in C# .NET

Encapsulation is one of the important building blocks in object-oriented programming. It is a mechanism in which data and code which manipulate this data are enclosed into a single unit. This unit can be a class in c# which allows the developer to hide data by making variables inaccessible outside class and providing functions, which can be accessed outside this class, to access these variables.

The idea is to hide the internals of an object behind specific methods i.e. restrict the direct access to some of an object’s component. In c# class, there are properties that allow class variables to be exposed by using get and set accessors.

Encapsulation

As you can see in the above code, in class Employee private data member Name is exposed by using public property Name with set and get accessors. Set is used to change the value of data members and Get is used to read the value of data members. Also, validation rules if any can be implemented in the set method when the value is being changed to ensure that new value is within desired limits. This way you can control access to the internal state of the object.

In encapsulation one single unit i.e. class is formed with data members that store the current state of the object and method that can used to access these data members.

Access modifiers & access levels

Encapsulation is implemented by using access specifiers. All classes and class members can specify what access level they provide to other classes by using access modifiers. Only one modifier can be used per class, method & data members. An access specifier specifies the scope and visibility of class members.

ModifierDefinition
publicThe type or member can be accessed by any other code in the same assembly or another assembly that references it.
privateThe type or member can only be accessed by code in the same class.
protectedThe type or member can only be accessed by code in the same class or in a derived class.
internalThe type or member can be accessed by any code in the same assembly, but not from another assembly.
protected internalThe type or member can be accessed by any code in the same assembly, or by any derived class in another assembly.
private protectedThe type or member can be accessed by code in the same class or in a derived class within the base class assembly.

Encapsulation – c# .NET example

Here is an example of an encapsulated class Employee in which we have specified properties Name & Total Experience to be specified by User. Employee Id is generated internally in class and generation logic is not exposed to the user and also user can only read Employee Id and cannot change it. Similarly, salary is also calculated internally in class, and the user can access employee salary by GetSalary method.

    class Employee
    {
        private string name;
        private long id;
        private double yearsofexperience;

        public Employee()
        {
            id = EmployeeService.GetEmployeeId();
        }

        public long Id
        {
            get { return id; }
        }
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public double YearsOfExperience
        {
            get { return yearsofexperience; }
            set { yearsofexperience = value; }
        }
        
        public double GetEmployeeSalary()
        {
            double salary = 0;
            if (yearsofexperience == 0) salary = 50000;
            else if (yearsofexperience <= 5) salary = yearsofexperience * 100000;
            else if (yearsofexperience > 5) salary = yearsofexperience * 1.5 * 100000;

            return salary;
        }
    }

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

In below example, Employee class has been used and by specifying Name & Experience of Employee user is able to get Id & salary without worrying much about the internals of Employee class

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void CreateEmployee()
        {
            Employee employee = new Employee();
            employee.Name = txtEmployeeName.Text;
            employee.YearsOfExperience = Convert.ToInt32(txtEmployeeSalary.Text);

            txtEmployeeId.Text = employee.Id;
            txtEmployeeSalary.Text = employee.GetSalary().ToString();
        }

        private void btnCreateEmployee_Click(object sender, EventArgs e)
        {
            CreateEmployee();
        }
    }

Advantages of Encapsulation

  • The internal implementation of the class is hidden from the user of the class. The user is exposed to just specific methods of the class.
  • It provides control over the data by allowing developers based on their requirements to consciously provide set or get methods for a private data member.
  • It makes easier to perform unit testing of the class.

Summary

Encapsulation is a fundamental block of object-oriented programming and it prevents access to implementation details. Encapsulation enables the programmer to implement the desired requirement for the level of access to the internals of the object.

It not only allows the programmer to control which attributes can be accessed or modified but also allows to limit changes as per the requirements.

Reference: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/object-oriented-programming

You can also check my another Article explaining ASP.NET Core caching – https://procodeguide.com/programming/aspnet-core-caching/