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)));