簡體   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