Cargando...

Fundamentos de Inyección de Dependencias en C#

Aprende los fundamentos de Inyección de Dependencias en C#, gestionando dependencias y logrando bajo acoplamiento.

En el desarrollo de software, la Inyección de Dependencias (DI) es un patrón de diseño importante utilizado para gestionar dependencias. Con DI, las clases reciben los objetos que necesitan desde el exterior en lugar de crearlos internamente. Esto hace que el código sea más flexible, comprobable y mantenible. En el ecosistema de C# y .NET Core, la DI está soportada de forma nativa.


¿Qué es una dependencia?

Una clase depende de otra cuando necesita su funcionalidad. Por ejemplo, un OrderService puede necesitar una instancia de ILogger al crear un pedido. Si OrderService usa directamente new Logger(), queda fuertemente acoplado a la clase Logger.


public class OrderService
{
    private readonly Logger _logger = new Logger();

    public void CreateOrder(string product)
    {
        _logger.Log($"Pedido creado: {product}");
    }
}

public class Logger
{
    public void Log(string message) => Console.WriteLine(message);
}

En este enfoque, el Logger no puede ser reemplazado. Si desea usar otro sistema de registro, debe modificar el código de OrderService. Aquí es donde entra en juego la Inyección de Dependencias.


Solución con Inyección de Dependencias

Con DI, las dependencias se abstraen y se proporcionan desde el exterior (a través del constructor o de parámetros de métodos). De esta forma, OrderService depende de la interfaz ILogger, y el mundo exterior decide qué logger usar.


public interface ILogger
{
    void Log(string message);
}

public class ConsoleLogger : ILogger
{
    public void Log(string message) => Console.WriteLine("[Consola] " + message);
}

public class FileLogger : ILogger
{
    public void Log(string message) => 
        System.IO.File.AppendAllText("log.txt", message + "\n");
}

public class OrderService
{
    private readonly ILogger _logger;

    // Inyección por constructor
    public OrderService(ILogger logger)
    {
        _logger = logger;
    }

    public void CreateOrder(string product)
    {
        _logger.Log($"Pedido creado: {product}");
    }
}

class Program
{
    static void Main()
    {
        // Se puede elegir un logger diferente
        ILogger logger = new ConsoleLogger();
        var service = new OrderService(logger);

        service.CreateOrder("Portátil");
    }
}

En este ejemplo, OrderService ya no está vinculado a una clase Logger específica; solo depende de la interfaz ILogger. Por lo tanto, ConsoleLogger o FileLogger pueden utilizarse fácilmente.


Tipos de Inyección de Dependencias


Uso de DI en .NET Core

En las aplicaciones .NET Core, se dispone de un contenedor de DI integrado. Los servicios se registran en Program.cs o Startup.cs, y se inyectan automáticamente en las clases que los necesiten.


var builder = WebApplication.CreateBuilder(args);

// Registrar servicios
builder.Services.AddScoped<ILogger, ConsoleLogger>();
builder.Services.AddScoped<OrderService>();

var app = builder.Build();

app.MapGet("/order", (OrderService service) =>
{
    service.CreateOrder("Teléfono");
    return "Pedido procesado.";
});

app.Run();

Aquí, el contenedor DI crea automáticamente una instancia de ConsoleLogger para OrderService y la inyecta.


Ventajas


TL;DR

  • Inyección de Dependencias: Las clases reciben los objetos que necesitan desde el exterior.
  • Inyección por constructor: El enfoque más común; las dependencias se pasan a través del constructor.
  • .NET Core: Proporciona un contenedor de DI integrado.
  • Beneficios: Permite una arquitectura más flexible, comprobable y fácil de mantener.

Artículos relacionados