Tja*_*art 28 c# dependency-injection asp.net-core
我需要在我的Startup类中实例化的Singleton类中访问我的数据库.似乎直接注入它会导致处理掉的DbContext.
我收到以下错误:
无法访问已处置的对象.对象名称:'MyDbContext'.
我的问题有两个:为什么这不起作用,如何在单例类实例中访问我的数据库?
这是我的Startup类中的ConfigureServices方法:
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>();
}
Run Code Online (Sandbox Code Playgroud)
这是我的控制器类:
public class TestController : Controller
{
private FunClass _fun;
public TestController(FunClass fun)
{
_fun = fun;
}
public List<string> Index()
{
return _fun.GetUsers();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的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;
}
}
Run Code Online (Sandbox Code Playgroud)
Spo*_*ook 64
原始来源:https : //entityframeworkcore.com/knowledge-base/51939451/how-to-use-a-database-context-in-a-singleton-service-
由于默认情况下 DbContext 是有作用域的,因此您需要创建作用域来访问它。它还允许您正确处理其生命周期 - 否则您会长时间保留 DbContext 的实例,不推荐这样做。
public class Singleton : ISingleton
{
private readonly IServiceScopeFactory scopeFactory;
public Singleton(IServiceScopeFactory scopeFactory)
{
this.scopeFactory = scopeFactory;
}
public void MyMethod()
{
using(var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<DbContext>();
// when we exit the using block,
// the IServiceScope will dispose itself
// and dispose all of the services that it resolved.
}
}
}
Run Code Online (Sandbox Code Playgroud)
Joe*_*tte 18
它不起作用的原因是因为.AddDbContext扩展将每个请求添加为作用域.每个请求的范围通常是您想要的,并且通常每个请求将调用一次保存更改,然后dbcontext将在请求结束时处理.
如果你真的需要在dbContext内部使用a singleton,那么你的FunClass类应该依赖于IServiceProvider而DbContextOptions不是直接依赖于它DbContext,这样你就可以自己创建它.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
也就是说,我的建议是仔细考虑你是否真的需要你的FunClass成为一个单身人士,我会避免这种情况,除非你有一个很好的理由让它成为一个单身人士.
我完全意识到这个解决方案不是正确的方法。请不要做我多年前在这里所做的事情。事实上,根本不要注入单例 DbContext。
解决方案是调用 AddSingleton,并在 Startup 类的方法参数中实例化我的类:
services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"])));
Run Code Online (Sandbox Code Playgroud)
解决方案是更改我的 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);
}
}
Run Code Online (Sandbox Code Playgroud)
然而,正如许多人警告的那样,在单例类中使用 DbContext 可能是一个非常糟糕的主意。我在实际代码中的使用非常有限(不是示例 FunClass),但我认为如果您这样做,最好找到其他方法。
如前所述,早期.AddDbContext扩展是将其添加为每个请求的范围。所以DI不能实例化Scoped对象来构造Singleton一个。
你必须MyDbContext自己创建和处理实例,这样更好,因为 DbContext 必须在使用后尽早处理。要传递连接字符串,您可以Configuration从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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在Startup.cs配置DbContextOptionBuilder和注册你的单身人士:
var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection"));
services.AddSingleton(new FunClass(optionsBuilder.Options));
Run Code Online (Sandbox Code Playgroud)
它有点脏,但效果很好。
您可以在 DbContext 上使用此参数:
ServiceLifetime.Singleton
services.AddDbContext<EntityContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")), ServiceLifetime.Singleton);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20613 次 |
| 最近记录: |