Design Patterns & Practices  

Dependency Injection DotNet

What is Dependency Injection?

Dependency Injection (DI) is a design pattern where an object receives the components it depends on from outside, rather than creating them inside itself.

Why DI?

  • Loose Coupling: Your classes depend on interfaces, not concrete classes, making it easier to change parts of your app.
  • Testability: Easily inject mocks when unit testing.
  • Maintainability: Change implementations without changing your business logic.
  • Flexibility: Swap implementations with minimal impact.

Core Principles Behind DI

  • Inversion of Control (IoC): "Don’t call us, we’ll call you." Instead of your code controlling everything, you delegate control to a container or framework that injects dependencies.
  • Dependency Inversion Principle (DIP): High-level modules should depend on abstractions (interfaces), not on low-level modules (concrete classes).

How to Implement DI (Constructor Injection example)  

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email sent: " + message);
}

public class NotificationService
{
    private readonly IMessageSender _messageSender;
    public NotificationService(IMessageSender messageSender)
    {
        _messageSender = messageSender;
    }
    public void Notify(string message) => _messageSender.Send(message);
}

class Program
{
    static void Main()
    {
        IMessageSender sender = new EmailSender();  // Create implementation outside
        var notifier = new NotificationService(sender); // Inject to consumer
        notifier.Notify("Dependency Injection is great!");
    }
}

Using DI Containers in .NET Framework 4.8

  • Popular containers: Unity, Autofac, Ninject.
  • Help automate the process of wiring dependencies.
  • Example with Unity:
    IUnityContainer container = new UnityContainer();
    container.RegisterType<IMessageSender, EmailSender>();
    container.RegisterType<NotificationService>();
    
    var notifier = container.Resolve<NotificationService>();
    notifier.Notify("Hello from DI container!");
    

Common Use Cases of DI

  • Logging and monitoring services.
  • Data repositories and database contexts.
  • External API clients.
  • Authentication and authorization management.
  • Scheduled/background jobs.

Benefits vs. Drawbacks

Benefits Drawbacks
Clear separation of concerns More setup code initially
Easier testing and mocking Learning curve for DI concepts
Flexibility to change dependencies Small runtime overhead (usually negligible)