【问题标题】:"Cannot access a disposed context" when trying to access EF Core DbContext in Startup ExceptionHandler尝试在 Startup ExceptionHandler 中访问 EF Core DbContext 时“无法访问已释放的上下文”
【发布时间】:2021-05-26 22:18:15
【问题描述】:

我有一个 .NET Core 3.0 应用程序。在我的 Startup.cs Configure 方法中,我正在处理意外错误并发送电子邮件通知。效果很好。

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&lt;IUserService&gt;(); 抛出以下内容:

无法访问已处置的对象。对象名称:'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 或忘记使用awaitStartup.cs 中的 Configure 方法显然是 void,但我不确定如果这确实是问题,可能有什么解决方法。

感谢任何意见。谢谢大家。

【问题讨论】:

    标签: c# entity-framework .net-core dependency-injection entity-framework-core


    【解决方案1】:

    这更多是一个设计问题,因为在解析和注入所需依赖项时,它们将使用初创公司的服务提供者,而不是请求的服务提供者。

    我首先建议通过委托中当前请求的上下文解决服务

    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
    }
    

    【讨论】:

    • 这太好了,谢谢。不幸的是,我目前正在将IEmailService 注入IUserService。我知道这是一种气味,但重构它是待办事项清单上的一些项目。我现在就试试看!
    • @Santi 那么我建议您检查您当前的设计,因为这会导致循环依赖问题
    • 这就像一个魅力。谢谢!是的,最初布局的服务运行良好并且完全是单向关系,但是随着我扩展应用程序,很明显它们现在需要快速重构。我计划尽快解决这个问题。
    猜你喜欢
    • 2016-12-06
    • 2021-06-29
    • 2021-06-24
    • 2021-03-29
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    • 2021-01-06
    相关资源
    最近更新 更多