繁体   English   中英

.NET Core 2.1 DbContext ObjectDisposedException 依赖注入

[英].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()方法中,所以它并没有真正帮助我。

任何人都知道如何解决或我可以在哪里搜索来解决这个问题?

提前致谢!

传递给ConfigureIServiceProvider实例是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并为您创建一个。 一旦scopeCityOfIdeasDbContext ,这个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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM