Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

19 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 20 de enero de 2026
Desarrollador inyectando componentes en un sistema

Normalmente, cuando registramos un servicio en el inyector de dependencias de .NET, lo hacemos una única vez. A veces registramos directamente el servicio, aunque lo habitual es que lo asociemos a una interfaz, de forma que en futuro podamos sustituir la implementación sin tener que cambiar el resto del código:

// Registramos el servicio asociado a una interfaz:
builder.Services.AddScoped<IInvoiceIssuer, InvoiceServices>();

Sin embargo, nada impide que el mismo servicio lo registremos varias veces asociándolo a distintas abstracciones, sobre todo si tenemos nuestras clases de servicio son muy extensas y estamos haciendo segregación de interfaces (la "I" de los principios SOLID):

// Registramos el servicio asociado a varias interfaces, de forma
// que en otros servicios sólo será necesario inyectar la interfaz 
// concreta que necesitemos:
builder.Services.AddScoped<IInvoiceIssuer, InvoiceServices>();
builder.Services.AddScoped<IInvoiceSender, InvoiceServices>();
builder.Services.AddScoped<IInvoicePrinter, InvoiceServices>();
...

Pero fijaos que esto está introduciendo un sutil problema: cada vez que se inyecte una de estas interfaces, se creará una nueva instancia de InvoiceServices, lo cual, además de consumir más recursos, puede ser un problema si a su vez el servicio depende de otros componentes que no puedan o deban ser compartidos:

public class DoSomething
{
    public DoSomething(IInvoiceIssuer issuer, IInvoiceSender sender, IInvoicePrinter printer)
    {
        // Aquí se habrán creado tres instancias de InvoiceServices
    }
}

¿Cómo podemos solucionarlo?

Para estos casos, podemos usar el parámetro implementationFactory que encontramos en los métodos AddScoped(), AddSingleton() y AddTransient() de IServiceCollection. Este parámetro es un delegado que podemos usar para crear la instancia a retornar cuando se requiera un objeto del tipo solicitado. Pero lo más interesante es que, dado que recibe como parámetro una referencia al proveedor de servicios, podemos utilizarlo para obtener la instancia de otro servicio ya registrado:

builder.Services.AddScoped<IInvoiceIssuer, InvoiceServices>(
    services => 
    {
        // Usamos "services" para obtener el servicio ya registrado,
        //  en vez de instanciar uno nuevo cada vez
        ...
    }   
);

En nuestro caso, podríamos registrar el servicio InvoiceServices una única vez de forma genérica, definiendo su tiempo de vida (singleton, scoped, transient) y sin asociarlo a ninguna interfaz concreta. Luego, a la hora de registrar las distintas abstracciones, usar el proveedor de servicios para obtener una instancia del tipo registrado:

// Registramos el servicio una única vez:
builder.Services.AddScoped<InvoiceServices>();

// A continuación, usamos para cada interfaz la factoría, 
// que retorna la instancia del servicio registrado:
builder.Services.AddScoped<IInvoiceIssuer, InvoiceServices>(
    services => services.GetRequiredService<InvoiceServices>()
);

builder.Services.AddScoped<IInvoiceSender, InvoiceServices>(
    services => services.GetRequiredService<InvoiceServices>()
);

builder.Services.AddScoped<IInvoicePrinter, InvoiceServices>(
    services => services.GetRequiredService<InvoiceServices>()
);

Obviamente, si se crean o no instancias nuevas de InvoiceServices dependerá de cómo se haya registrado este servicio en el contenedor de dependencias. Por ejemplo, si está registrada como singleton, se devolverá siempre la misma instancia, independientemente de cómo se haya registrado la interfaz; si es transient, se creará una nueva instancia cada vez que se solicite. En el caso anterior, como se ha registrado como scoped, se devolverá la misma instancia en el ámbito actual (si estamos en una petición ASP.NET Core, mientras ésta esté siendo procesada).

Sencillo, ¿eh?

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!