.net - iservicecollection - mvc dependency injection
¿Es "seguro" almacenar en caché IServiceProvider para la vida útil de una aplicación? (2)
No desea inyectar su contenedor IoC en ningún lado. Esa es una mala práctica que permite una codificación descuidada y dificulta las pruebas unitarias, entre muchas otras razones.
En su lugar, introduzca una fábrica que se pueda inyectar y cree un contexto a pedido:
public interface IDbContextFactory<TContext>
{
TContext Create();
}
public class DbContextFactory<TContext> : IDbContextFactory<TContext>
where TContext : DbContext
{
private readonly Func<TContext> _contextCreator;
public DbContextFactory(Func<TContext> contextCreator)
{
_contextCreator = contextCreator;
}
public TContext Create()
{
return _contextCreator();
}
}
Ahora si lo inyectas en tu Foo
:
public class Foo
{
private readonly IDbContextFactory<MyContext> _contextFactory;
public Foo(IDbContextFactory<MyContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void bar() {
{
using (var context = _contextFactory.Create())
{
// use your freshly instantiated context
}
}
}
Cualquier marco de inyección de dependencia decente puede resolver el parámetro Func<TContext>
de DbContextFactory
, y pasar un func allí que crea una instancia por solicitud.
Estoy usando ASP.NET Core y su contenedor DI integrado. Estoy usando una biblioteca de terceros (NLog) que no puedo cambiar.
Mi clase Foo
tiene una dependencia (por inyección de constructor).
public class Foo {
private readonly IMyContext _context;
public Foo(IMyContext context) { _context = context; }
// etc.
}
Sin embargo, la biblioteca almacena en caché la instancia de Foo
durante la duración de la aplicación (que está fuera de mi control). Eso significa que también almacena en caché la dependencia. Y esa dependencia no se debe almacenar en caché, porque se trata de un contexto EF que debe tener un alcance.
Una alternativa es inyectar IServiceProvider
y luego crear instancias yo mismo.
public class Foo {
private readonly IServiceProvider _sp;
public Foo(IServiceProvider sp) { _sp = sp; }
// etc.
public void bar() {
var context = _sp.GetService<IMyContext>();
// use it
}
}
Pero como antes, esa instancia de IServiceProvider
se almacenaría en caché durante toda la vida de la aplicación.
¿Eso es "seguro"? ¿Hay repercusiones negativas que deba conocer?
Adición a la excelente solución de @ CodeCaster.
Es importante cambiar Func<Dependency>
a Func<Owned<Dependency>>
. De lo contrario, el contenedor no devolverá nuevas instancias cada vez, incluso si está utilizando una fábrica de delegados.
Probablemente tiene algo que ver con el hecho de que un componente de larga duración (singleton) depende de un componente de corta duración (por transacción). Func<>
previene la falla de dependencia cautiva, pero para que la fábrica también le proporcione nuevas instancias cada vez, necesita ser Owned<>
.
No entiendo por qué, y parece contrario a la intuición, pero así es como funciona.