Dependency Injection in C#

In this article, we will discuss dependency injection in C# and how to implement Dependency Injection in C#. One of the design patterns used most commonly in real-time applications today is dependency injection. So, as a developer, you need to understand when and why to employ the C# Dependency Injection Design Pattern. In C#, the Dependency Injection Design Pattern enables us to create software components that are loosely connected. To put it another way, we could argue that the Dependency Injection Design Pattern is utilized to loosen the tightly coupled nature of the software components. As a result, we can easily handle additional complexity and future changes in our program.

We can create loosely coupled code by using the software design pattern known as Dependency Injection (DI). The use of DI can significantly reduce the tight coupling between software parts. We can manage future updates and other complexity in our software more effectively thanks to DI. To make code maintainable, DI was created.

Let's first define tight coupling and loose coupling in software development before we can grasp the Dependency Injection Design Pattern using C#. So let's first understand these two ideas.



Dependency Injection Design Pattern in C#:


Software designers can create better applications by using the Dependency Injection (DI) paradigm. It enables us to create maintainable, loosely connected programs. By technically injecting those requirements at run time rather than design time, dependency injection decreases the number of hard-coded dependencies between your classes. With the C# Dependency Injection Design Pattern, we can add an object from one class to another class that depends on it. To eliminate dependencies between items, the Dependency Injection design pattern is currently the most used design pattern.

IoC (Inversion of Control) is implemented using the design pattern known as Dependency Injection (DI). It permits the introduction of dependencies outside of classes and gives such dependencies to classes in various ways. We shift the construction and binding of the dependent objects outside of the class that depends on them by using the Dependency Injection Design Pattern.


The dependency Injection pattern involves 3 types of classes:

Client Class: The Client class (dependent class) is a class that depends on the service class.

Service Class: The Service class (dependency) is a class that provides service to the client class.

Injector Class: The Injector class injects the service class object into the client class.


Look at the following diagram for a better understanding of the Dependency injection pattern.


di_types.png


The injector class produces an object of the service class and injects that object into a client class, as shown in the figure above. The Dependency Injection Design Pattern does this by separating the client class's role in object creation from that of the service class.



Tight Coupling in Software Design Pattern:


Tight coupling is when a group of classes are highly dependent on one another. When two objects are tightly coupled, they are reliant on one another. Thus, it is said that two classes have a tight coupling when they are dependent on one another. In that case, changing the dependent object requires likewise changing the classes that make use of the dependent object. Making these adjustments is not too tough to handle if your application is small, but if it is a large enterprise-level application, it is incredibly challenging to handle.


Given below is an example of Tight coupling in C#.


 public class SqlDbImplementation

{


 public void AddNewStudent(Student student)

{

    var query = $"insert into Student(StudentId,StudentName,Age,Qualification) " +

        $"values({student.Id},'{student.Name}',{student.Age},{student.Qualification}";

   

     Db.insert(query);

    _return query;


}

}


namespace StuControllers  

{


 // This his how class are dependant on each other through tight coupling    

class StudentController : ControllerBase

{

       

    [HttpPost()]

public ActionResult AddNewStudent([FromBody] Student student)

{

     var sqldb = new SqlDbImplementation();

   

     sqldb.AddNewStudent(student);

    return Ok("data inserted successfully");

}

  }

}



As mentioned above code In the previous version, it is the way we were used to implementation. If we change anything in one class so it will affect every single class as they depend on each other. Suppose, if we tried to change the database from SQL server to Oracledb in the future then we will have to implement the same class as well as in the controller class where we have implemented instance of our dependant class.

After creating a new service class this is how our code will look in Tight Coupling:


 [HttpPost()]
public ActionResult AddNewStudent([FromBody] Student student)

{

     var sqldb = new OracleDbImplementation();  

     sqldb.AddNewStudent(student);

    return Ok("data inserted successfully");

}




Disadvantages  of Tight Coupling:


  • Tight coupling is usually bad because it reduces flexibility and re-usability of code and it makes changes much more difficult and impedes testability etc.


  • In Tight Coupling, if we change the dependent object, then we also need to change the classes where this dependent object is used.


  •  In Tight Coupling, if your application is a small one, then it is not that difficult to handle but if you have a big enterprise-level application, then it’s really very difficult to handle to make these changes.


  • This scenario arises when a class assumes too many responsibilities, or when one concern is spread over many classes rather than having its own class.


  • Tightly coupled code is difficult to maintain and test, it reduces flexibility and makes the code inapplicable for reuse.



Loose coupling in Software Design Pattern:


When two objects are loosely coupled, they are independent of one another. It means that changing one thing will not modify another. We can easily manage upcoming modifications owing to the loosely connected nature of software development, which also makes it possible for us to control how complex the program is. In the case of loose coupling which is achieved using a design that promotes single responsibility and separation of concerns and it’s also very easy to maintain as well as easy to test.


Let’s review a code example showing how to implement Constructor injection in C#:

Example:

Let’s create a web API that includes the feature dependency injection through the following steps.


Step 1:

First, we will create here model class named Student where we’ll have properties.

 

 namespace Di_Demo.Models

{

    public class Student

    {

        public int Id { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public string Qualification { get; set; }

    }

}




Step 2:

Second, we will create an interface named IstudentService.

IstudentService.cs


 using Di_Demo.Models;

 namespace Di_Demo.Services

{

    public interface IStudentService

    {

       

        void AddNewStudent(Student  student);     

    }

}



The above-mentioned code is an interface that is connected with our Controller as well as other classes, through this interface we can achieve dependency injection.


Step 3:

Third, We will create a ServiceStudent class where we will have an implementation of methods.

StudentService.cs


 namespace Di_Demo.Services

{

    public class StudentService : IStudentService

    {

        private readonly IRepository _repository;

        public StudentService(IRepository repository)

        {

            _repository = repository ?? throw new ArgumentNullException(nameof(repository));

        }

 public void AddNewStudent(Student student)

{

    string query = $"insert into Student(StudentId,StudentName,Age,Qualification) " +

        $"values({student.Id},'{student.Name}',{student.Age},{student.Qualification}";

    _repository.InsertUpdateDelete(query);

    return;

 }


 }


 }



As we can see in the above-given example the dependency injection is providing the objects that an object needs, instead of having it construct the object itself. Here is the implementation of the IStudentService one method, but in case maybe in the future we need Oracledb instead of Sqlserverdb then we don’t need to change anything in the controller, and it won't affect the controller, Also we don’t need to touch or change anything in the StudentService class the only changes we will have to do is all about to create new services.

Given below is the code where have to add our new services.


Step 4:

In the fourth step, whatever service class we will create will be added here through AddTransient.


 // Add services to the container.

 builder.Services.AddTransient<IStudentService, StudentService>();

   



Step 5:

In the fifth step, we will create a Controller class named StudentController which is dependent on the interface.

StudentController.cs


namespace
Di_Demo.Controllers


{

    [Route("api/[controller]")]

    [ApiController]

    public class StudentController : ControllerBase

    {

        private readonly IStudentService _studentService;

        public StudentController(IStudentService studentService)

       

        {

            _studentService = studentService ?? throw new ArgumentNullException(nameof(studentService));

        }

  [HttpPost()]

 public ActionResult AddNewStudent([FromBody] Student student)

{

    _studentService.AddNewStudent(student);

    return Ok("data inserted successfully");

}



The above-mentioned code is justifying classes can communicate through the interface and be injected into the constructor. When we try to convert tight coupling into loose coupling that time we use dependency injection.


If we are changing the implementation of IStudentService from StudentService.cs to StudentServiceV2 then we don’t have to change any code in the controller.

We need to change only in program.cs


 builder.services.AddTransient<IStudentService, StudentServiceV2>();




Advantages of using Loose Coupling:


Writing loosely coupled code has the following advantages :


  • Loosely coupled code allows for an easy substitution of components without many changes to the entire code base.


  • In Loose Coupling, if we change one object then it will not affect another object.


  • In Loose coupling, One module won’t break other modules.


  • Loose coupling Code is easier to maintain and enhances testability.


  • Loosely coupled code Gets less affected by changes in other components.


  • Loose coupling implies that services are independent so that changes in one service will not affect any other.



Let’s look at the diagram below to understand the concepts of tight coupling and loose coupling.


wr.png

rt.png



Types of Dependency Injection in C#:


There are three ways you can use the dependency injection pattern in your C# applications. These are:

  • Constructor Injection

  • Property Injection

  • Method Injection


Three alternative methods are used by the injector class to inject the dependency object into a class. Below, we will learn about each dependency injection in detail. 


Constructor Injection:


When the Injector injects the dependency object (i.e. service) through the client class constructor, then it is called Constructor Dependency Injection. Constructor injection is one of the most widely used methods for implementing dependency injection when developing applications. By using constructor dependency injection, you can inject the dependent class object through the constructor. The injected component can then be used anywhere across different class methods.


Property Injection:


When the Injector injects the dependency object (i.e. service) through the public property of the client class, then it is called Property Dependency Injection. This is also called the Setter Injection. Property injection is the process of injecting dependent class objects through the property. It is the preferred method when a class has optional dependencies, as dependency can be injected without changing the object state.


Method Injection:


When the Injector injects the dependency object (i.e. service) through a public method of the client class, then it is called Method Dependency Injection. In this case, the client class implements an interface that declares the method(s) to supply the dependency object and the injector uses this interface to supply the dependency object (i.e. service) to the client class. The process of injecting the dependent class object through methods is known as method injection. When a certain class method is not needed by the entire class, the dependence can be injected into that method using method injection.



Conclusion:


We've already seen what dependency injection is and how to use it in C# to build classes that are loosely coupled. There could be components in a real-world software application that are dependent on other components. Effectively managing those components can be a tiresome and time-consuming chore for any coder. Dependency injection assists the developer in this case by allowing them to modify the software application's workflow and write loosely coupled code.

Hope you enjoyed reading this article and found it useful. Please share your thoughts and recommendations in the comment section below.


Share This Post

Linkedin
Fb Share
Twitter Share
Reddit Share

Support Me

Buy Me A Coffee