C# Dependency Injection: Everything You Need to Know About Dependency Injection In C#

Dependency Injection (DI) is a design pattern in software development that enables objects to obtain their dependencies from external sources instead of creating them internally. In simpler terms, DI is a way of achieving loose coupling in software applications.

When an object has a dependency, it requires a reference to the dependent object to function properly. Traditionally, objects would create their dependencies, making it difficult to change or update them if needed.

However, with DI, we can decouple objects from their dependencies and allow the dependencies to be injected into the object from the outside.

csharp-dependency-injection
C# Dependency Injection

Introduction of Dependency Injection Design Pattern in C#

Dependency Injection is an important design pattern that facilitates the creation of reusable, maintainable, and testable code in C#. Using this pattern, developers can easily swap out implementations, control the lifecycle of objects, and reduce dependencies.

What is the Purpose of the Dependency Injection Design Pattern in C#?

The main purpose of the Dependency Injection design pattern in C# is to achieve loose coupling

It means that objects are not tightly bound to their dependencies, making it easier to modify and maintain code. 

Dependency Injection (DI) also makes it easier to write unit tests because dependencies can be mocked or replaced with test doubles.

What is Tight Coupling in Software Development?

Tight coupling in software development refers to a situation where two or more components are highly dependent on each other. It makes difficult to change one component without affecting the other. In tight coupling, if any change is made to one component, it may also require changes to the other component.

What is Loose Coupling in Software Development?

Loose coupling in software development refers to a situation where two or more components are independent of each other. Loose coupling makes modifying or updating one component easy without affecting the other. In loose coupling, we can make changes to one component without needing to change the other.

Different ways to achieve Dependency Injection in C#

There are three different ways to achieve dependency injection in C#: 

  • Constructor injection. 
  • Method injection.
  • Property injection.

Implementing dependency injection using constructor injection

Constructor injection is the most common way of implementing dependency injection in C#. In this approach, dependencies are passed to the object through its constructor.

Here is the code example of constructor dependency injection in C# using Customer.cs, ICustomerDAL.cs, CustomerDAL.cs, and CustomerBL.cs classes in a console application:

First, let’s create a Customer.cs class which will be used as a model:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

Next, Let’s define the interface ICustomerDAL

The ICustomerDAL interface defines the contract for the data access layer that interacts with the database to add and retrieve customers.

public interface ICustomerDAL
{ 
    // Method to add customer to the database
    void Add(Customer customer);
  // Method to retrieve customer from the database
    Customer Get(int id);
}

Now let’s implement the ICustomerDAL interface in CustomerDAL.cs class

The CustomerDAL class implements the ICustomerDAL interface and provides the implementation for the Add() and Get() methods that interact with the database.

public class CustomerDAL : ICustomerDAL
{
    List<Customer> listCustomers = new List<Customer>();
    // Implement the Add method
    public void Add(Customer customer)
    {
        // Currently, we will use hard-coded data, but in a practical scenario
        //, we would implement database logic in this section to add the customer in the database.
        listCustomers.Add(customer);
        Console.WriteLine($"Added customer '{customer.Name}' to database.");
    }
    // Implement the Get method to retrieve customer data
    public Customer Get(int customerId)
    {
       // Specify the ID of the customer to retrieve customer data.
        Customer customer = listCustomers.FirstOrDefault(c => c.Id == customerId);
        return customer;      
    }
}

Define the CustomerBL.cs class.

The CustomerBL class represents the business logic layer that depends on the ICustomerDAL interface to add and retrieve customers record.

public class CustomerBL
{
 // Declare a private readonly field of ICustomerDAL interface
    private readonly ICustomerDAL _customerDAL;
 // Constructor to inject the ICustomerDAL dependency
    public CustomerBL(ICustomerDAL customerDAL)
    {
        _customerDAL = customerDAL;
    }

  // Method to add customer record
    public void AddCustomer(Customer customer)
    {
        _customerDAL.Add(customer);
    }
   // Method to retrieve customer data
    public Customer GetCustomer(int id)
    {
        return _customerDAL.Get(id);
    }
}

Define the Program class.

class Program
{
    static void Main(string[] args)
    {
        // Create an instance of CustomerDAL class
        ICustomerDAL customerDAL = new CustomerDAL();
        // Create an instance of CustomerBL class and inject the ICustomerDAL dependency
        CustomerBL customerBL = new CustomerBL(customerDAL);
        // Create a new Customer object
        Customer customer = new Customer { Id = 1, Name = "Shekh Ali", Email = "shekh.ali@xyz.com" };
        // Add the customer to the database
        customerBL.AddCustomer(customer);
        // Retrieve the customer from the database
        Customer retrievedCustomer = customerBL.GetCustomer(1);
        // Display the retrieved customer information
        Console.WriteLine($"Retrieved customer: {retrievedCustomer.Name} ({retrievedCustomer.Email})");
    }
}

Output:

DI_Example

What is Constructor Injection?

Constructor Injection is a design pattern in which a class’s dependencies are injected via its Constructor rather than being created inside the class itself. It is a form of Dependency Injection (DI) used to achieve loose coupling between classes and their dependencies.

a. How does Constructor Injection work?

In Constructor Injection, the class that requires a dependency declares its dependency as a parameter in its Constructor. The dependency is injected into the Constructor as an argument when the class is instantiated. It allows the class to use the dependency without being tightly coupled.

b. Benefits of Constructor Injection:

  • Constructor injection provides loose coupling between classes and their dependencies.
  • It allows for easier testing of classes, as dependencies can be mocked or stubbed.
  • It promotes the Single Responsibility Principle, as classes only need to worry about their own responsibilities rather than creating and managing dependencies.
  • It increases code maintainability, as changes to dependencies can be made outside of the class and do not require changes to the class itself.
  • It facilitates code extensibility, as we can easily add new dependencies by creating a new implementation of the required interface and injecting it into the class’s Constructor.

implementing dependency injection using method injection

Here is a C# code implementing dependency injection using method injection:

Example:

using System;
using System.Collections.Generic;
using System.Linq;

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public interface ICustomerDAL
{
    void Add(Customer customer);
    Customer Get(int id);
}

public class CustomerDAL : ICustomerDAL
{
    List<Customer> listCustomers = new List<Customer>();
    public void Add(Customer customer)
    {
        // Currently, we will use hard-coded data, but in a practical scenario
        //, we would implement database logic in this section to add the customer in the database.
        listCustomers.Add(customer);
        Console.WriteLine($"Added customer '{customer.Name}' to database.");
    }

    public Customer Get(int customerId)
    {
        // Specify the ID of the customer to retrieve
        Customer customer = listCustomers.FirstOrDefault(c => c.Id == customerId);
        return customer;
    }
}

public class CustomerBL
{
    public void AddCustomer(Customer customer, ICustomerDAL customerDAL)
    {
        customerDAL.Add(customer);
    }

    public Customer GetCustomer(int id, ICustomerDAL customerDAL)
    {
        return customerDAL.Get(id);
    }
}

class Program
{
    static void Main(string[] args)
    {
        ICustomerDAL customerDAL = new CustomerDAL();
        CustomerBL customerBL = new CustomerBL();

        Customer customer = new Customer { Id = 1, Name = "Amit", Email = "amit@example.com" };
        customerBL.AddCustomer(customer, customerDAL);

        Customer retrievedCustomer = customerBL.GetCustomer(1, customerDAL);
        Console.WriteLine($"Retrieved customer: {retrievedCustomer.Name} ({retrievedCustomer.Email})");
    }
}

Output:

Added customer 'Amit' to database.
Retrieved customer: Amit (amit@example.com)

In the above code, we have made the following changes to implement dependency injection using method injection:

  • In the CustomerBL class, we have added ICustomerDAL as a parameter to both the AddCustomer and GetCustomer methods.
  • We have removed the Constructor from the “CustomerBL” class, as we no longer need it to inject the dependency.
  • In the Main method, we have passed the ICustomerDAL object as a parameter to both the AddCustomer and GetCustomer methods.

Using method injection, we can inject the dependency at runtime and change it whenever needed. It makes our code more flexible and easier to maintain.

Advantages of dependency injection in C#:

  1. Reduces Coupling: Dependency injection reduces coupling between the classes by separating the responsibility of creating dependencies from the class that requires them. This makes the code more flexible, reusable, and easier to maintain.
  2. Increases Testability: With dependency injection, dependencies can be easily replaced with mock objects or stubs during testing, making it easier to test the individual code units in isolation. It also makes it easier to identify and fix bugs in the code.
  3. Promotes Code Reusability: With dependency injection, you can reuse existing code by injecting it into other classes that require it. It will reduce the amount of code you need to write and promotes modularization and reusability of the code.
  4. Facilitates Separation of Concerns: Dependency injection helps to separate the concerns of different parts of the code. It separates the responsibilities of creating and managing dependencies from the business logic, which makes the code more organized and easier to understand.
  5. Improves Scalability: Dependency injection makes adding new features and functionality to an application easier. Since the code is organized into smaller, more modular components, it is easier to manage and scale as the application grows.

Dependency injection is a powerful design pattern that can help you write more flexible, maintainable, and testable code. 

Reducing coupling between the classes, promoting code reusability, and facilitating the separation of concerns can help you build more robust and scalable applications.

FAQs

Q: What is dependency injection in C#?

Dependency injection is a design pattern that reduces dependencies between classes and components in a software system. It allows for creating loosely-coupled code that is easier to maintain and test.

Q: What are the different types of dependency injection in C#?

There are three types of dependency injection in C#:
Constructor injection, property injection, and method injection.

Q: What is constructor injection in C#?

Constructor injection is a type of dependency injection where dependencies are passed to a class through its constructor. This is the most common type of dependency injection used in C#.

Q: What is property injection in C#?

Property injection is a type of dependency injection where dependencies are set through public properties of a class. This type of injection is less common than constructor injection and can lead to more complex code.

Q: What is method injection in C#?

Method injection is a type of dependency injection where dependencies are passed to a method as parameters. This type of injection is useful for methods that need to use a dependency only occasionally or in specific scenarios.

Q: What are the advantages of using dependency injection in C#?

Dependency injection can make code more modular and easier to test and maintain. It can also reduce dependencies between classes and improve overall code quality.

Q: What are some common dependency injection frameworks in C#?

Some popular dependency injection frameworks in C# include Autofac, Ninject, and Unity. These frameworks provide tools and utilities to manage and inject dependencies in a software system.

References: MSDN-DI in .NET

Recommended Articles:

c20e93cf99b4a0eb1e4a099de6c2c300?s=250&r=g
4 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments