简体   繁体   中英

Asp.net core: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection

I am developing a web api project. Nowadays, I have a problem when calling an endpoint. I do not constantly get an error when calling this endpoint, for example if I am calling 30 times a day, I get this error 2 times. This endpoint just select the data and working async. I tried all the solutions I found on the internet but it didn't work. Error: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

 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>();
}

Note: I'm using .net core 3.1

--UPDATE

As far as I can see from the log, the ResponseWrapperFilter class causes this error. But since I couldn't catch this error while debugging, I couldn't find a reason.

    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                          
                    });
            }             
        }           
    }
}

As far as I can see from the log,the bold line is causing of the error (p.GetSetMethod().Invoke(v, new[] { CompanyIdCompany });)

As this problem only shows sporadically, a likely cause of the problem is an (accidental) spawn background operation running parallel to the request operation. This parallel operation has access to the request's DbContext instance.

NOTE: In case the error would persist after an initial successful operation, this would be an indication that the DbContext is held captive by one of its consumers; in other words, a Captive Dependency .

In most cases the background operation finishes before the request ends, and thus before the DbContext is disposed of. In some cases, however, there is a delay in that (accidental) parallel operation, causing it to access the DbContext after the request has ended, and thus after the DbContext was disposed of by ASP.NET Core.

This might have happened because you forgot to await an asynchronous operation. When you forget to await such operation, it starts running in parallel. Running such code in parallel, however, is often problematic, especially when dealing with objects such as DbContext , as they are not thread-safe.

So analyze the stack trace to find the place where you forgot to await the operation. For instance:

public async Task DoSomethingAsync()
{
     await this.DoSomethingNiceAsync();
     this.DoSomethingElseAsync(); // forgot to await
}

private async Task DoSomethingElseAsync()
{
    // Makes use of dbcontext
    await this.dbContext.GetEntitiesAsync();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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