我正在使用.NET Core 2.1和Entity Framework创建n层MVC应用程序。还有一个托管的MQTT队列,我的应用程序在该队列上作为客户端侦听。我还利用了依赖注入。这非常有效,直到将消息推送到队列并且我想将该消息保存到数据库。一旦发生这种情况,我会收到以下ObjectDisposedException
错误消息:
无法访问已处置的对象。导致此错误的常见原因是,处理从依赖项注入中解决的上下文,然后稍后尝试在应用程序中的其他位置使用相同的上下文实例。如果在上下文上调用Dispose()或将上下文包装在using语句中,则可能会发生这种情况。如果使用依赖项注入,则应让依赖项注入容器负责处理上下文实例。对象名称:“ xxxDbContext”。
我可以单击“继续”,然后该应用程序将继续运行。他仅在从队列收到的第一条消息上引发异常。与控制器/管理器/存储库进行的所有其他操作都很好。我的代码如下:
Startup.cs
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.
}
管理器只是将其直接发送到发生错误消息的存储库中。
Repository.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);
}
DbContext.cs
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");
}
}
我已经读过this,但这些情况似乎都不适合我。而且,当我在Dispose()
中打印stacktrace时,它是在Main()
方法中发生的,因此它并没有真正帮助我。
有人知道如何解决这个问题,或者在哪里可以找到解决办法?
谢谢!
答案 0 :(得分:2)
传递到IServiceProvider
的{{1}}实例是作用域的,这意味着Configure
完成后,它会被框架处置-任何 scoped 服务也会在此过程中被处理。
在您的示例中,您请求一个Configure
的实例(作用域为 ),然后尝试在您的IIdeationManager
类中使用该实例(有效,一个 singleton )。在您尝试使用MqttBroker
的实现时,由DI创建并关联的IIdeationManager
的 scoped 实例已被废弃,因此{{1} }引发异常。
为了解决此问题,您可以采用一种通用模式,该模式在单例需要访问有范围的服务时使用:创建一个范围,解析该服务,使用该服务,然后处置该范围。松散地,看起来会像这样:
CityOfIdeasDbContext
当您请求实现ObjectDisposedException
时,DI系统会(最终)看到它需要一个范围限定的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
被处置,该CityOfIdeasDbContext
实例也将被处置。
为了使它在您的示例中起作用,您的scope
可以将CityOfIdeasDbContext
的一个实例放入其构造函数中,并将其用于创建上面显示的范围(它仍然可以包含{假设{1}}本身是一个单例。
应该传递给MqttBroker
类的IServiceProvider
实例应该 not 不是传递给IConfiguration
的{{1}}-这是IServiceProvider
完成后,已经确定了作用域,并且已经清理过了,这实际上是您刚开始遇到的问题。为此,请使用MqttBroker
,它是根提供程序,没有作用域。
答案 1 :(得分:0)
在我尝试处置dbcontext对象(如.net经典分层结构存储库设计模式案例)之前,我曾遇到过相同的问题,但是在.net核心中,足以使它解决问题。因为它为每个请求处理,所以您不需要手动处理dbcontext。另外,IServiceCollection中的adddbcontext方法将其范围设置为默认值。 Reference