我需要在我的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>();
}
这是我的控制器类:
public class TestController : Controller
{
private FunClass _fun;
public TestController(FunClass fun)
{
_fun = fun;
}
public List<string> Index()
{
return _fun.GetUsers();
}
}
这是我的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;
}
}
答案 0 :(得分:17)
它不起作用的原因是因为.AddDbContext
扩展名将每个请求添加为作用域。每个请求的范围通常是您想要的,通常每个请求都会调用一次保存更改,然后dbcontext
将在请求结束时处理。
如果您确实需要在dbContext
内使用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;
}
}
那就是说,我的建议是仔细考虑你是否真的需要你的FunClass成为一个单身人士,我会避免这种情况,除非你有充分的理由让它成为一个单身人士。
答案 1 :(得分:3)
解决方案是调用AddSingleton,我的类在我的Startup类的method参数中实例化:
services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"])));
解决方案是更改我的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);
}
}
然而,由于多人警告过,在单例类中使用DbContext可能是一个非常糟糕的主意。我的用法在实际代码中非常有限(不是示例FunClass),但我认为如果你这样做,最好找到其他方法。
答案 2 :(得分:3)
由于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.
}
}
}
答案 3 :(得分:3)
IServiceScopeFactory
可以做到这一点。
public class BattleManager : IBattleManager {
private readonly IServiceScopeFactory scopeFactory;
public BattleManager(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.
}
}
}
DbContext
的行为就像它在 using 语句中具有 Transient
范围一样。
答案 4 :(得分:3)
您可以在 DbContext 上使用此参数:
<块引用>ServiceLifetime.Singleton
services.AddDbContext<EntityContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")), ServiceLifetime.Singleton);
答案 5 :(得分:0)
无需重载MyDbContext的ctor。
services.AddSingleton(s=>new FunClass(new MyDbContext(new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(configuration.GetConnectionString("DefaultConnection")).Options)));
答案 6 :(得分:0)
正如前面提到的.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();
}
}
}
在Startup.cs
中配置DbContextOptionBuilder
并注册您的单身人士:
var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection"));
services.AddSingleton(new FunClass(optionsBuilder.Options));
有点脏,但是效果很好。
答案 7 :(得分:-1)
asp.net核心的简单代码
private readonly IConfiguration _configuration; public IdeasController( IConfiguration configuration) { _configuration = configuration; } var optionsBuilder = new DbContextOptionsBuilder<_YourContext>(); optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")); using (var context = new _YourContext(optionsBuilder.Options)) { var output = context.Users.Select(x=>x.Name).ToList(); }