pattern net asp c# dependency-injection asp.net-core

c# - pattern - Utilice DbContext en ASP.Net Singleton Injected Class



asp net core web api mongodb (4)

Actualizar

Soy plenamente consciente de que esta solución no es la forma correcta de hacerlo. Por favor, no hagas lo que hice aquí hace tantos años. De hecho, no inyecte un Singleton DbContext en absoluto.

Vieja respuesta

La solución fue llamar a AddSingleton con mi clase instanciada en el parámetro del método en mi clase de Inicio:

services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"])));

La solución fue cambiar mi clase DbContext:

public class MyContext : IdentityDbContext<ApplicationUser> { private string connectionString; public MyContext() { } public MyContext(DbContextOptions options, string connectionString) { this.connectionString = connectionString; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // Used when instantiating db context outside IoC if (connectionString != null) { var config = connectionString; optionsBuilder.UseSqlServer(config); } base.OnConfiguring(optionsBuilder); } }

Sin embargo, como han advertido varias personas, usar un DbContext en una clase singleton podría ser una muy mala idea. Mi uso es muy limitado en el código real (no en el ejemplo FunClass), pero creo que si lo hace, sería mejor encontrar otras formas.

Necesito acceder a mi base de datos en una clase Singleton instanciada en mi clase de Inicio. Parece que inyectarlo directamente da como resultado un DbContext que está dispuesto.

Obtuve el siguiente error:

No se puede acceder a un objeto desechado. Nombre del objeto: ''MyDbContext''.

Mi pregunta es doble: ¿por qué esto no funciona y cómo puedo acceder a mi base de datos en una instancia de clase singleton?

Aquí está mi método ConfigureServices en mi clase de inicio:

public void ConfigureServices(IServiceCollection services) { // code removed for brevity services.AddEntityFramework().AddSqlServer().AddDbContext<MyDbContext>( options => { var config = Configuration["Data:DefaultConnection:ConnectionString"]; options.UseSqlServer(config); }); // code removed for brevity services.AddSingleton<FunClass>(); }

Aquí está mi clase de controlador:

public class TestController : Controller { private FunClass _fun; public TestController(FunClass fun) { _fun = fun; } public List<string> Index() { return _fun.GetUsers(); } }

Aquí está mi FunClass:

public class FunClass { private MyDbContext db; public FunClass(MyDbContext ctx) { db = ctx; } public List<string> GetUsers() { var lst = db.Users.Select(c=>c.UserName).ToList(); return lst; } }


Como se mencionó .AddDbContext extensión .AddDbContext lo agrega según el alcance por solicitud. Por lo tanto, DI no puede crear una instancia del objeto Scoped para construir Singleton one.

Debe crear y eliminar la instancia de MyDbContext usted mismo, es aún mejor porque DbContext debe eliminarse después de usarlo lo antes posible. Para pasar la cadena de conexión, puede tomar la Configuration de la clase de Startup :

public class FunClass { private DbContextOptions<MyDbContext> _dbContextOptions; public FunClass(DbContextOptions<MyDbContext> dbContextOptions) { _dbContextOptions = dbContextOptions; } public List<string> GetUsers() { using (var db = new MyDbContext(_dbContextOptions)) { return db.Users.Select(c=>c.UserName).ToList(); } } }

En Startup.cs configure DbContextOptionBuilder y registre su singleton:

var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>(); optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")); services.AddSingleton(new FunClass(optionsBuilder.Options));

Está un poco sucio, pero funciona muy bien.


La razón por la que no funciona es porque la extensión .AddDbContext lo agrega según el alcance por solicitud. El alcance por solicitud generalmente es lo que desea y, por lo general, los cambios guardados se llamarían una vez por solicitud y luego el dbcontext se eliminaría al final de la solicitud.

Si realmente necesita usar un dbContext dentro de un singleton , entonces su clase FunClass probablemente debería depender de IServiceProvider y DbContextOptions lugar de depender directamente del DbContext , de esa manera puede crearlo usted mismo.

public class FunClass { private GMBaseContext db; public FunClass(IServiceProvider services, DbContextOptions dbOptions) { db = new GMBaseContext(services, dbOptions); } public List<string> GetUsers() { var lst = db.Users.Select(c=>c.UserName).ToList(); return lst; } }

Dicho esto, mi consejo sería considerar cuidadosamente si realmente necesita que su FunClass sea un singleton, lo evitaría a menos que tenga una muy buena razón para hacerlo un singleton.


No es necesario sobrecargar el ctor de MyDbContext.

services.AddSingleton(s=>new FunClass(new MyDbContext(new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(configuration.GetConnectionString("DefaultConnection")).Options)));