Dependency Injection in C#



Dependency Injection (DI) is one of the most important concepts in modern C# (.Net) development, especially for writing clean, testable, and maintainable code. DI is a design pattern that helps you to create loosely coupled, flexible and testable applications.

What is Dependency Injection?

In C# .Net, they have good support for DI, which stands for Dependency Injection. It is a technique for achieving inversion of control (IoC) between classes and their dependencies. DI in .NET is a built-in part of the framework, along with configuration logging and the design pattern.

In Inversion of Control (IoC), the control of object creation and dependency management is taken away from the class itself and given to an external source (like a framework or container).
A dependency is an object that another object depends on.

Let's understand with the following example. Suppose we have a class called "College" that manages technical events. Inside the College class, we create an object of another class called "TechEvents".

Example 1: Without Inversion of Control

// Example 1: Without Inversion of Control
// Tightly Coupled Classes
using System;

public class TechEvents {
   public void OrganizeEvent() {
      Console.WriteLine("Organizing Tech Event...");
   }
}

public class College {
   private TechEvents _techEvent;

   public College() {
      // College is responsible for creating TechEvents
      _techEvent = new TechEvents();
   }

   public void StartEvent() {
      _techEvent.OrganizeEvent();
   }
}

public class Program {
   public static void Main() {
      College college = new College();
      college.StartEvent();
   }
}

Following is the output

Organizing Tech Event...

Explanation

  • Here, "College' and "TechEvents" are tightly coupled, one cannot exist without the other.
  • If the TechEvents class changes, we must also modify and recompile the College class.
  • College is directly controlling how TechEvents objects are created.
  • If we want to organize a different event like FootballEvent or PartyEvent, we'll have to change the College class.

Example 2: With Inversion of Control

// Example 2: With Inversion of Control 
// Loosely Coupled Using Interface and Injection
using System;

public interface IEvent {
   void OrganizeEvent();
}

public class TechEvents : IEvent {
   public void OrganizeEvent() {
      Console.WriteLine("Organizing Tech Event...");
   }
}

public class FootballEvent : IEvent {
   public void OrganizeEvent() {
      Console.WriteLine("Organizing Football Event...");
   }
}

public class College {
   private readonly IEvent _eventService;

   // Constructor Injection — dependency provided from outside
   public College(IEvent eventService) {
      _eventService = eventService;
   }

   public void StartEvent() {
      _eventService.OrganizeEvent();
   }
}

public class Program {
   public static void Main() {
      // You can easily switch between events without modifying College
      IEvent techEvent = new TechEvents();
      College college1 = new College(techEvent);
      college1.StartEvent();

      IEvent footballEvent = new FootballEvent();
      College college2 = new College(footballEvent);
      college2.StartEvent();
   }
}

Following is the output

Organizing Tech Event...
Organizing Football Event...

Explanation

  • Here, "College" doesn't create or control TechEvents.
  • We can inject any event type, FootballEvent, PartyEvent, or TechEvents without changing the College code.
  • The control of object creation has been inverted (shifted to the caller or framework).

Type of Dependency Injection

There are mainly three type of Dependency Injection in C#

  • Constructor Injection
  • Property or Setter Injection
  • Method Injection

1. Constructor Injection

In C#, Constructor Injection is the most common and suggested form of dependency injection. In this type, the dependency is provided through a class constructor. When the object is created, its required dependencies are passed as parameters to the constructor.

Example

In the following example, we demonstrate how the Constructor Injection (CI) will work.

using System;
public interface IMessageService {
   void SendMessage(string message);
}

public class EmailService : IMessageService {
   public void SendMessage(string message) {
      Console.WriteLine("Email sent: " + message);
   }
}

public class Notification {
   private readonly IMessageService _messageService;

   // Dependency is injected through the constructor
   public Notification(IMessageService messageService) {
      _messageService = messageService;
   }

   public void Notify(string message) {
      _messageService.SendMessage(message);
   }
}

class Program {
   static void Main() {
      IMessageService emailService = new EmailService();
      Notification notification = new Notification(emailService);
      notification.Notify("Hello from Constructor Injection");
   }
}

Following is the output

Email sent: Hello from Constructor Injection

Advantages:Constructor injection ensures dependencies are always provided at object creation, allowing immutability and clear contracts.

2. Property (Setter) Injection

In C#, Property Injection, dependencies are assigned to public properties after the object is created. This allows flexibility if dependencies need to be changed later, but it also means the class may exist in an incomplete state until properties are set.

Example

In the following example, we demonstrate how the Property Injection (PI) will work.

using System;
public interface IMessageService {
   void SendMessage(string message);
}

public class EmailService : IMessageService {
   public void SendMessage(string message) {
      Console.WriteLine("Email sent: " + message);
   }
}

public class Notification {
   public IMessageService MessageService { get; set; }

   public void Notify(string message) {
      MessageService.SendMessage(message);
   }
}

class Program {
   static void Main() {
      Notification notification = new Notification();
      // Inject via property
      notification.MessageService = new EmailService();
      notification.Notify("Hello from Property Injection");
   }
}

Following is the output −

Email sent: Hello from Property Injection

Advantages − Property Injection provides flexibility to change dependencies dynamically.
Disadvantages − The class might not function properly if the property isn't initialized.

3. Method Injection

In C#, Method Injection, dependencies are passed directly to the method that needs them. It is useful when only specific methods require certain dependencies instead of the entire class.

Example

In the following example, we demonstrate how the Method Injection (MI) will work.

using System;

public interface IMessageService {
   void SendMessage(string message);
}

public class EmailService : IMessageService {
   public void SendMessage(string message) {
      Console.WriteLine("Email sent: " + message);
   }
}

public class Notification {
   public void Notify(IMessageService messageService, string message) {
      messageService.SendMessage(message);
   }
}

class Program {
   static void Main() {
      IMessageService emailService = new EmailService();
      Notification notification = new Notification();
      notification.Notify(emailService, "Hello from Method Injection");
   }
}

Following is the output −

Email sent: Hello from Method Injection

Advantages − Method Injection provides detailed control and is perfect when dependency is only required for one method.
Disadvantages − Can lead to repetitive code if the same dependency is required in multiple methods.

Advantages of Dependency Injection

Following is the advantage of using the DI −

  • It promotes loose coupling
  • It improve code maintainability
  • It optimize the testability using mock objects
  • It maximizes productivity and minimizes rework

Conclusion

Dependency injection simplifies class interaction by removing tight coupling between them. It enables you to create applications that are clean, modular, and easy to test. By using .NET's built-in DI, you can effectively handle dependencies and also make your application more flexible and manageable.

Advertisements