Understanding Dependency Injection In ASP.NET Core

One of the important features of the asp.net core 3.1 cross-platform framework is “Build in Dependency injection." What is meant by the term “dependency injection”?

Let’s see the common definition we find when we search online.

  1. Dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service).
  2. Dependency injection is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself.
  3. Dependency injection is a practice where objects are designed in a manner where they receive instances of the objects from other pieces of code, instead of constructing them internally. This means that any object implementing the interface that is required by the object can be substituted in without changing the code, which simplifies testing, and improves decoupling.

There’s a lot more definitions, so let’s understand the term “DEPENDENCY” and “INJECTING DEPENDENCY” with a simple representation.

When we pass object of class B in class A, Methods of Class A use the object to perform its function. So, we say Class A has dependency on the Class B object.

Injecting Dependency means the dependency (object of class B) is pushed into class A from outside.

Let’s emphasize the statement “object pushed from outside."

  • This means we shouldn’t initiate dependencies (object) using new operator from inside of a class.

    Example:


     
  • Instead, we can inject by passing dependency into the constructor, or via setter. There are many more ways to inject dependency, too.

    Example: Injection via constructor

I will share a different blog on dependency injection where we’ll see “Types of dependency injection” with examples.

One of the points mentioned above is that we shouldn’t initiate objects within a class using new keywords. Though this is a valid implementation, and we can access members and methods of one class into another, it’s not recommended most of the time. We need to understand that one of the major advantages of using dependency injection that is it allows “loose coupling” and avoids “tight coupling."

Coupling," in simple words, means interdependency. Coupling is divided into two types (loose coupling and tight coupling), on the basis how the elements are in relationship to one another, or how they are interdependent with each other.

Let’s understand difference between the types of coupling and why loose coupling is preferable over tight coupling.

Tight coupling vs loose coupling

In tight coupling, two components in a system are highly dependent on each other, and change in one component effects another. Menawhile, in loose coupling, there is less interdependency between components. This is how these two types of coupling differ.

Let’s look at an example.

I have created a class named calculator1.cs with one simple function to find factorials.

namespace DIDemo.Repository {
    public class Calculator1 {
        public int Factorial(int n) {
            int fact = 1;
            for (int i = n; i >= 1; i--) {
                fact *= i;
            }
            return fact;
        }
    }
}

First let’s go with the tight coupling approach.

namespace DIDemo.Controllers {
    public class HomeController: Controller {
        private readonly Calculator1 _calc;
        public HomeController() {
            _calc = new Calculator1(); //tight coupling
        }
        public int Index() {
            return _calc.Factorial(4);
        }
    }
}

Within the FindFact() method, we called object method Factorial(). Now, let’s look at another scenario. In the future I will want to make a Factorial method with optimized code, and I want to keep the already implemented code as well. So, for this, I need to create a different method or different class with the same method. But we can see that we now have to make changes within the dependent class as well to cope with new changes.

Let’s fcreate a new class, named Calculator2.cs, with the same function to find factorial with the recursive approach.

namespace DIDemo.Repository {
    public class Calculator2 {
        public int Factorial(int n) {
            if (n == 0) {
                return 1;
            }
            return n * Factorial(n - 1);
        }
    }
}

Now if I want to use this method, I need to make changes in the dependent class as well.

I need to initiate the object for the calculator2 class instead of the calculator1 class.

While developing a small application, one can easily identify and debug changes. When it comes to large scale applications, it takes more time to identify changes, and there are high chances of error-prone code occurring.

Here we can see a huge disadvantage: if any changes made in the called class then changes within the dependent class due to high interdependency. Alternatively, this reduces the flexibility of the reusability of code. To overcome this, we choose the loose coupling approach, where we reduce interdependency.

In Asp.net core, we will make use of the interface to achieve a dependency injection, which allows for loose coupling. As in the example mentioned above, we will try to understand the flow and how dependencies are injected.

Let’s create asp.net core mvc application.

Once the application is ready, let’s create classes named calculator1 and claculator2 with an interface ICalculator. We mainly used interface to break the dependencies between classes (also called decoupling). Instead of being interdependent, both the classes depend upon the interface.

For more information, please go through the advantages of interfaces.

namespace DIDemo.Repository {
    public interface ICalculator {
        int Factorial(int n);
    }
}
namespace DIDemo.Repository {
    public class Calculator1: ICalculator {
        public int Factorial(int n) {
            int fact = 1;
            for (int i = n; i >= 1; i--) {
                fact *= i;
            }
            return fact;
        }
    }
}
namespace DIDemo.Repository {
    public class Calculator2: ICalculator {
        public int Factorial(int n) {
            if (n == 0) {
                return 1;
            }
            return n * Factorial(n - 1);
        }
    }
}

Once done, let’s inject the dependency through constructor in our home controller.

namespace DIDemo.Controllers {
    public class HomeController: Controller {
        private readonly ICalculator _calc;
        public HomeController(ICalculator calc) {
            _calc = calc;
        }
        public int Index() {
            return _calc.Factorial(4);
        }
    }
}

Interface automatically creates the instance of a class - but which one?

One of the main features of Asp.net core is shown here. This feature provides built-in Containers for automatic dependency injections -to be more specific, DI (Dependency injection) containers, or Ioc (Inversion of control) containers. Some people use the term IoC Container, while others use the term DI container, but both terms indicate the same thing. So, don't be confused by the terminology.

We can check the ConfigureServices in the startup class, in which we can register application services with a built-in container by using IserviceCollection interface.

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

Let’s try to run the application.

We got an error page. This is expected; as you go through the exception, it’s mentioned as “unable to resolve service." We are getting this message as we have not yet registered our services. So let’s do it:

public void ConfigureServices(IServiceCollection services) {
    services.AddControllersWithViews();
    //services.AddScoped<ICalculator, Calculator1>();
    services.AddScoped < ICalculator, Calculator2 > ();
}

Let’s execute. We are getting output.

While registering services we can specify which class instance it should create while injecting dependency in another class. There is a clear advantage here - instead of making changes in different classes and methods, creating new instances each and every time, we can make small changes to increase code reusability and reducing classes' interdependencies.

If you check out the code used within the configure service, One method is called AddScoped().

But what is AddScoped()? For now, just worry about registering our services within the ConfigureService() method. The built-in IoC container manages the lifetime of a registered service. The Ioc container has three types of lifetimes: singleton, transient, and scoped, to support the same asp.net core framework. It provides three extension methods: Addsingleton(), Addtransient() and Addscoped() within Iservicecolletion. As of now, we have used the AddScoped() method to register the service in the above example.

I will continue the topic in my next blog. I will try to upload it in the coming week. It’s a huge topic to cover everything within one post. Still, I have tried to cover most of this topic in the simplest way possible with easy examples and easy wordings. Thank you for investing your precious time. I hope it’s worth it.

Happy coding! Happy learning!