[英]Error: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection
[英]Asp.net core: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection
我正在开发一个 web api 项目。 如今,我在调用端点时遇到了问题。 调用此端点时,我不会经常收到错误,例如,如果我每天调用 30 次,我会收到 2 次此错误。 这个端点只是 select 数据和异步工作。 我尝试了在互联网上找到的所有解决方案,但没有奏效。 错误:无法访问已处置的 object。 此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。 如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。 如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(options => options.UseMySql(Environment.GetEnvironmentVariable("MY_DB_CONNECTION")), ServiceLifetime.Transient);
services.DIRegisterer();
}
public static IServiceCollection DIRegisterer(this IServiceCollection services)
{
services.AddScoped<MyService>();
}
注意:我使用的是 .net 核心 3.1
- 更新
据我从日志中可以看出,ResponseWrapperFilter class 导致了这个错误。 但是由于我在调试时无法捕捉到这个错误,所以我找不到原因。
public class ResponseWrapperFilter : IActionFilter, IOrderedFilter
{
private MYContext myContext;
private IMemoryCache cache;
private readonly TimeSpan _defaultCacheTimespan = new TimeSpan(0, 5, 0);
private void FillNavigationProperties(object v, int recursionLevel)
{
if (recursionLevel > 2)
return;
if (v.GetType().FullName == "System.DateTime" || v.GetType().FullName == "System.String"
|| v.GetType().FullName == "Newtonsoft.Json.Linq.JObject")
return;
if (v != null && v.GetType().FullName.StartsWith("System.Collections.Generic.List"))
{
foreach (var x in (IList)v)
{
foreach (var y in x.GetType().GetProperties())
{
if (x.GetType().FullName == "System.DateTime" || x.GetType().FullName == "System.String"
|| x.GetType().FullName == "Newtonsoft.Json.Linq.JObject")
continue;
var prop = y.GetGetMethod().Invoke(x, null);
if (prop != null)
{
FillNavigationProperties(prop, recursionLevel + 1);
}
else
{
TryToFillNavigationProperty(x, y);
}
}
}
}
}
private void TryToFillNavigationProperty(object v, PropertyInfo p)
{
if (p.PropertyType == typeof(Company) || p.PropertyType.IsSubclassOf(typeof(Company)))
{
if (v.GetType().GetProperties().Where(_ => _.Name == p.Name + "Id").Any())
{
var CompanyId = (ulong)v.GetType().GetProperties().Where(_ => _.Name == p.Name + "Id").First().GetGetMethod().Invoke(v, null);
var cacheKey = "Company->Id->" + CompanyId.ToString();
if (cache.TryGetValue(cacheKey, out Company CompanyIdCompany))
{
p.GetSetMethod().Invoke(v, new[] { CompanyIdCompany });
}
else
{
CompanyIdCompany = myContext.Set<Company>().Where(_ => _.Id == CompanyId).FirstOrDefault();
cache.Set(cacheKey, CompanyIdCompany, _defaultCacheTimespan);
**p.GetSetMethod().Invoke(v, new[] { CompanyIdCompany });**
}
}
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is null)
{
myContext = context.HttpContext.RequestServices.GetRequiredService<MYContext>();
cache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>();
if (context.Result.GetType() == typeof(ObjectResult))
{
object current = ((ObjectResult)context.Result).Value;
if (current != null)
FillNavigationProperties(current, 0);
context.Result = new OkObjectResult(new MYResponse()
{
Data = current
});
}
}
}
}
据我从日志中可以看出,粗线是导致错误的原因 (p.GetSetMethod().Invoke(v, new[] { CompanyIdCompany });)
由于此问题只是偶尔显示,因此问题的可能原因是与请求操作并行运行的(意外)生成后台操作。 此并行操作可以访问请求的DbContext
实例。
注意:如果错误在初始成功操作后仍然存在,这表明
DbContext
被它的一个消费者控制; 换句话说,一个Captive Dependency 。
在大多数情况下,后台操作在请求结束之前完成,因此在DbContext
被释放之前完成。 然而,在某些情况下,该(意外)并行操作存在延迟,导致它在请求结束后访问DbContext
,因此在DbContext
内核处理 DbContext 之后。
这可能是因为您忘记await
异步操作而发生的。 当您忘记await
此类操作时,它会开始并行运行。 然而,并行运行这样的代码通常是有问题的,尤其是在处理诸如DbContext
类的对象时,因为它们不是线程安全的。
因此分析堆栈跟踪以找到您忘记await
操作的地方。 例如:
public async Task DoSomethingAsync()
{
await this.DoSomethingNiceAsync();
this.DoSomethingElseAsync(); // forgot to await
}
private async Task DoSomethingElseAsync()
{
// Makes use of dbcontext
await this.dbContext.GetEntitiesAsync();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.