繁体   English   中英

Asp.net 内核:无法访问已处置的 object。 此错误的常见原因是处理从依赖注入中解析的上下文

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM