簡體   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