![](/img/trans.png)
[英]How do I configure a DbContext with dependency injection with .Net Core 2.1?
[英].NET Core 2.1 DbContext ObjectDisposedException Dependency Injection
我正在使用 .NET Core 2.1 和 Entity Framework 制作一个 n 层 MVC 应用程序。 还有一个托管的 MQTT 队列,我的应用程序作为客户端侦听该队列。 我也使用依赖注入。 这完美地工作,直到将消息推送到队列并且我想将该消息保存到数据库。 一旦发生这种情况,我会收到以下ObjectDisposedException
错误消息:
无法访问已处置的对象。 此错误的一个常见原因是处理从依赖注入解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。 如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。 如果你使用依赖注入,你应该让依赖注入容器来处理上下文实例。 对象名称:'xxxDbContext'。
我可以点击继续,之后应用程序继续工作。 他只在从队列收到的第一条消息上抛出异常。 控制器/管理器/存储库的所有其他操作都可以正常工作。 我的代码如下:
启动文件
public void ConfigureServices(IServiceCollection services)
{
services.AddDefaultIdentity<User>()
.AddEntityFrameworkStores<xxxDbContext>();
services.AddDbContext<xxxDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
));
// Some identity configuration omitted here
services.AddScoped<IIdeationRepository, IdeationRepository>();
services.AddScoped<IIdeationManager, IdeationManager>();
// Some other DI configuration omitted as well.
}
public Configure(IApplicationBuilder app, IHostingEnvironment env,
IApplicationLifetime applicationLifetime, IServiceProvider serviceProvider)
{
// Start MQTT
var broker = new MqttBroker(serviceProvider.GetService<IIdeationManager>(),
serviceProvider.GetService<IConfiguration>());
// On application exit terminate MQTT to make sure the connection is ended properly
applicationLifetime.ApplicationStopping.Register(() => broker.Terminate());
// Some default http pipeline code omitted
}
MqttBroker.cs
public MqttBroker(
[FromServices] IIdeationManager ideationManage,
[FromServices] IConfiguration configuration)
{
_ideationManager = ideationManager;
_configuration = configuration;
Initialize();
}
// Some code where I just parse the message and on receive send it to the
// ideation manager, this just works so I omitted it.
}
管理器只是将它直接发送到存储库,在那里出现错误消息。
存储库.cs
private xxxDbContext ctx;
public IdeationRepository(xxxDbContext xxxDbContext)
{
this.ctx = xxxDbContext;
}
// This method crashes with the error
public IdeationReply ReadIdeationReply(int id)
{
return ctx
.IdeationReplies
.Include(r => r.Votes)
.FirstOrDefault(r => r.IdeationReplyId == id);
}
数据库上下文
public class xxxDbContext : IdentityDbContext<User>
{
public DbSet<Ideation> Ideations { get; set; }
// Some more dbsets omitted
public CityOfIdeasDbContext(DbContextOptions<CityOfIdeasDbContext> options)
: base (options)
{
CityOfIdeasDbInitializer.Initialize(this, dropCreateDatabase: false);
}
// In configuring I just create a logger, nothing special
// In OnModelCreating I just setup some value converters for other tables
// than the ones I need here
internal int CommitChanges()
{
if (delaySave)
{
int infectedRecords = base.SaveChanges();
return infectedRecords;
}
throw new InvalidOperationException(
"No UnitOfWork present, use SaveChanges instead");
}
}
我读过这篇文章,但这些情况似乎都不适用于我。 当我在Dispose()
打印堆栈跟踪时,它发生在Main()
方法中,所以它并没有真正帮助我。
任何人都知道如何解决或我可以在哪里搜索来解决这个问题?
提前致谢!
传递给Configure
的IServiceProvider
实例是scoped ,这意味着它在Configure
完成后由框架处理 - 它创建的任何作用域服务也在此过程中处理。
在您的示例中,您正在请求一个IIdeationManager
实例(它是scoped ),然后尝试在您的MqttBroker
类(实际上是一个单例)中使用它。 到时候你试图使用你的执行IIdeationManager
,的范围的情况下CityOfIdeasDbContext
已创建并DI迷上了已处置等一个ObjectDisposedException
引发异常。
为了解决这个问题,您可以采用一种通用模式,当单例需要访问范围服务时使用该模式:创建范围,解析服务,使用服务,然后处置范围。 松散地,这看起来有点像这样:
using (var scope = serviceProvider.CreateScope())
{
var ideationManager = scope.ServiceProvider.GetService<IIdeationManager>();
// Do something with ideationManager.
}
// scope and all created disposable services have been disposed.
当您请求IIdeationManager
的实现时,DI 系统看到(最终)它需要一个作用域CityOfIdeasDbContext
并为您创建一个。 一旦scope
被CityOfIdeasDbContext
,这个CityOfIdeasDbContext
实例也会被CityOfIdeasDbContext
。
为了使其在您的示例中工作,您的MqttBroker
可以将IServiceProvider
的实例带入其构造函数,并使用它来创建我在上面显示的范围(它仍然可以采用IConfiguration
,因为它本身是一个单例) .
该IServiceProvider
应传递到实例MqttBroker
类不应该是IServiceProvider
传递到Configure
-这已经是作用域,正如我所描述,之后将被清理Configure
完成,这真的是你以前做的问题开始了。 为此,请使用app.ApplicationServices
,它是根提供程序并且没有范围。
我之前也遇到过同样的问题,之前我也试图处理 dbcontext 对象,如 .net 经典分层结构存储库设计模式案例,但在 .net 核心中足以使其作用域解决问题。 因为它为每个请求处理,所以您不需要手动处理 dbcontext。 此外, IServiceCollection 中的 adddbcontext 方法将其实现为默认范围。 参考
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.