簡體   English   中英

嘗試在 Startup ExceptionHandler 中訪問 EF Core DbContext 時“無法訪問已釋放的上下文”

[英]"Cannot access a disposed context" when trying to access EF Core DbContext in Startup ExceptionHandler

我有一個 .NET Core 3.0 應用程序。 在我的 Startup.cs Configure方法中,我正在處理意外錯誤並發送 email 通知。 它工作得很好。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IEmailService emailService)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(options =>
        {
            options.Run(
                async context =>
                {
                    var ex = context.Features.Get<IExceptionHandlerFeature>();
                    if (ex != null)
                    {
                        await emailService.SendErrorEmailAsync(context, ex);
                        context.Response.Redirect($"/Error/{context.Response.StatusCode}");
                    }
                });
        }
        );
        app.UseStatusCodePagesWithReExecute("/Error/{0}");
        app.UseHsts();
    }

    //...
}

我現在想通過包含一些關於用戶的信息來改進它。 為此,我嘗試了以下方法:

private readonly MyApplicationContext _dbContext;
private readonly IServiceProvider _services;

public EmailService(MyApplicationContext dbContext, IServiceProvider services) {
    _dbContext = dbContext;
    _services = services;
}

public async Task SendErrorEmailAsync(HttpContext context, Exception ex)
{
    var _userService = _services.GetRequiredService<IUserService>();
    var user = await _userService.GetCurrentUserAsync();

    //...build email, send, etc
}

var _userService = _services.GetRequiredService<IUserService>(); 拋出以下內容:

無法訪問已處置的 object。 Object 名稱:“IServiceProvider”。

作為一種解決方法,我想我可以嘗試直接調用數據庫而不是通過IUserService

public async Task SendErrorEmailAsync(HttpContext context, Exception ex)
{
    User user = null;
    var isLoggedIn = context.User.Identity.IsAuthenticated;
    if (isLoggedIn)
    {
        var idString = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
        if (idString != null)
        {
            var userId = Guid.Parse(idString);
            user = await _dbContext.Users.Include(u => u.Client).FirstOrDefaultAsync(u => u.Id == userId );
        }
    }

    //...build email, send email, etc
}

user = await _dbContext...拋出:

無法訪問已釋放的上下文實例。 此錯誤的一個常見原因是釋放從依賴注入中解析的上下文實例,然后嘗試在應用程序的其他地方使用相同的上下文實例。 如果您在上下文實例上調用“Dispose”或將其包裝在 using 語句中,則可能會發生這種情況。 如果你使用依賴注入,你應該讓依賴注入容器負責處理上下文實例。

在研究這個問題時,最常見的原因是返回async void或忘記使用await Startup.cs中的Configure方法顯然是一個void ,但我不確定如果這確實是問題,可能會有什么解決方法。

我很感激任何意見。 謝謝大家。

這更像是一個設計問題,因為在解析和注入所需的依賴項時,它們將使用初創公司的服務提供者,而不是請求的服務提供者。

我首先建議通過委托中的當前請求上下文解決服務

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
    } else {
        app.UseExceptionHandler(options => {
            options.Run(
                async context => {
                    var ex = context.Features.Get<IExceptionHandlerFeature>();
                    if (ex != null) {
                        IEmailService emailService = context.RequestServices.GetRequiredService<IEmailService>();
                        await emailService.SendErrorEmailAsync(context, ex);
                        context.Response.Redirect($"/Error/{context.Response.StatusCode}");
                    }
                });
        });
        app.UseStatusCodePagesWithReExecute("/Error/{0}");
        app.UseHsts();
    }

    //...
}

還要重構服務以避免傳遞服務提供者。

private readonly MyApplicationContext dbContext;
private readonly IUserService userService;

public EmailService(MyApplicationContext dbContext, IUserService userService) {
    this.userService = userService;
    this.dbContext = dbContext;
}

public async Task SendErrorEmailAsync(HttpContext context, Exception ex) {
    var user = await userService.GetCurrentUserAsync();
    
    //...build email, send, etc
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM