DEV Community

Cover image for Mastering Dependency Injection in C# and .NET Core: A Comprehensive Guide with Code Examples
Leandro Veiga
Leandro Veiga

Posted on

Mastering Dependency Injection in C# and .NET Core: A Comprehensive Guide with Code Examples

Dependency Injection (DI) is a fundamental design pattern that promotes loose coupling and enhances the testability and maintainability of your applications. In the realm of C# and .NET Core, DI is not just a recommended practice but a core feature of the framework itself. This comprehensive guide delves into the intricacies of Dependency Injection in C# and .NET Core, providing detailed explanations, code examples, and real-world use cases to help you master this essential pattern.

Table of Contents


What is Dependency Injection?

Dependency Injection is a design pattern that allows a class to receive its dependencies from an external source rather than creating them itself. In simpler terms, instead of a class instantiating the objects it needs, it receives them from the outside, typically through constructors, properties, or method parameters.

Example Without Dependency Injection

public class OrderService
{
    private EmailService _emailService;

    public OrderService()
    {
        _emailService = new EmailService();
    }

    public void PlaceOrder(Order order)
    {
        // Place order logic
        _emailService.SendConfirmationEmail(order);
    }
}
Enter fullscreen mode Exit fullscreen mode

Example With Dependency Injection

public class OrderService
{
    private readonly IEmailService _emailService;

    public OrderService(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public void PlaceOrder(Order order)
    {
        // Place order logic
        _emailService.SendConfirmationEmail(order);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the DI version, OrderService does not instantiate EmailService directly. Instead, it receives an implementation of IEmailService through its constructor, promoting loose coupling and better testability.


Benefits of Using Dependency Injection

  • Loose Coupling: Classes are less dependent on concrete implementations, making the system more modular and flexible.
  • Enhanced Testability: Dependencies can be easily mocked or stubbed during unit testing.
  • Maintainability: Changes in dependencies require minimal modifications to dependent classes.
  • Reusability: Services can be reused across different parts of the application without duplication.
  • Scalability: Facilitates managing complex applications by organizing dependencies effectively.

Dependency Injection in .NET Core

.NET Core comes with a built-in Dependency Injection container, making it seamless to implement DI in your applications.

Built-in DI Container

The built-in DI container in .NET Core supports constructor injection and can be configured in the Startup.cs file. While it is lightweight compared to other DI containers, it is sufficient for most applications. However, for more complex scenarios, you might opt for third-party containers like Autofac or Ninject.

Service Lifetimes

When registering services, you can specify their lifetimes:

  • Transient: A new instance is provided every time the service is requested.
  • Scoped: A single instance is provided per request.
  • Singleton: A single instance is created and shared throughout the application's lifetime.
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IEmailService, EmailService>();
    services.AddScoped<IOrderRepository, OrderRepository>();
    services.AddSingleton<ILogger, Logger>();
}
Enter fullscreen mode Exit fullscreen mode

Implementing Dependency Injection in a .NET Core Application

Let's walk through a practical example of implementing DI in a .NET Core Web API application.

Step-by-Step Example

Create a New .NET Core Web API Project

dotnet new webapi -n DependencyInjectionDemo
cd DependencyInjectionDemo
Enter fullscreen mode Exit fullscreen mode

Define Interfaces and Services

IEmailService.cs
public interface IEmailService
{
    void SendEmail(string to, string subject, string body);
}
Enter fullscreen mode Exit fullscreen mode
EmailService.cs
public class EmailService : IEmailService
{
    public void SendEmail(string to, string subject, string body)
    {
        // Logic to send email
        Console.WriteLine($"Sending Email to {to}: {subject}");
    }
}
Enter fullscreen mode Exit fullscreen mode
IOrderService.cs
public interface IOrderService
{
    void PlaceOrder(Order order);
}
Enter fullscreen mode Exit fullscreen mode
OrderService.cs
public class OrderService : IOrderService
{
    private readonly IEmailService _emailService;

    public OrderService(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public void PlaceOrder(Order order)
    {
        // Logic to place order
        _emailService.SendEmail(order.CustomerEmail, "Order Confirmation", "Your order has been placed.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Register Services in Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddTransient<IEmailService, EmailService>();
    services.AddScoped<IOrderService, OrderService>();

    // Swagger and other services...
}
Enter fullscreen mode Exit fullscreen mode

Inject Services into Controllers

OrderController.cs
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
    private readonly IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService;
    }

    [HttpPost]
    public IActionResult PlaceOrder([FromBody] Order order)
    {
        _orderService.PlaceOrder(order);
        return Ok("Order placed successfully.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Code Examples

Models

Order.cs
public class Order
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public string CustomerEmail { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Interfaces and Services

IEmailService.cs
public interface IEmailService
{
    void SendEmail(string to, string subject, string body);
}
Enter fullscreen mode Exit fullscreen mode
EmailService.cs
public class EmailService : IEmailService
{
    public void SendEmail(string to, string subject, string body)
    {
        // Simulate sending email
        Console.WriteLine($"Email sent to {to}: {subject} - {body}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Dependency Injection is a crucial design pattern for building modular, testable, and maintainable applications in .NET Core. By leveraging the built-in DI container, developers can effectively manage dependencies, enhance reusability, and create scalable applications.

Would you like to explore further customization or integrate additional features?

Top comments (0)