c# - 尝试在 Startup ExceptionHandler 中访问 EF Core DbContext 时“无法访问已释放的上下文”
问题描述
我有一个 .NET Core 3.0 应用程序。在我的 Startup.csConfigure
方法中,我正在处理意外错误并发送电子邮件通知。它工作得很好。
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>();
抛出以下内容:
无法访问已处置的对象。对象名称:“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
. 其中的Configure
方法Startup.cs
显然是 a 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
}
推荐阅读
- android - Moshi 多态JsonAdapterFactory
- javascript - 代码在 Codepen 中有效,但在我的计算机中无效
- python - django 上的监视列表系统
- python-3.x - 如何在类中嵌套多处理管理器字典
- django - 在投资组合中提交 Django 私钥是个问题吗?
- reactjs - 反应:无法使用 useContext 钩子
- java - 如何使我的对象池成为单例?
- search - 无法在厨师刀搜索节点中定义顶级字段“自动”
- snowflake-cloud-data-platform - 计算时如何修复雪花中的舍入误差?
- jquery - jQuery onlick 事件在 AjAX 成功调用中不起作用