Asp.net核心:无法访问已处置的对象.导致此错误的常见原因是处理从依赖项注入中解决的上下文 [英] Asp.net core: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection

查看:22
本文介绍了Asp.net核心:无法访问已处置的对象.导致此错误的常见原因是处理从依赖项注入中解决的上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个Web api项目.如今,调用端点时出现问题.调用此终结点时,我不会经常收到错误消息,例如,如果我一天打30次,则两次都会收到此错误消息.该端点仅选择数据并工作异步.我尝试了在互联网上找到的所有解决方案,但是没有用.错误:无法访问已处置的对象.导致此错误的常见原因是,处理从依赖项注入中解析出来的上下文,然后稍后尝试在应用程序中的其他位置使用相同的上下文实例.如果在上下文上调用Dispose()或将上下文包装在using语句中,则可能会发生这种情况.如果您正在使用依赖项注入,则应让依赖项注入容器来处理上下文实例.

  public void ConfigureServices(IServiceCollection服务){services.AddDbContext< MyContext>(options => options.UseMySql(Environment.GetEnvironmentVariable("MY_DB_CONNECTION")),ServiceLifetime.Transient);services.DIRegisterer();}公共静态IServiceCollection DIRegisterer(此IServiceCollection服务){services.AddScoped< MyService>();} 

注意:我正在使用.net core 3.1

-更新

据我从日志中看到的,ResponseWrapperFilter类会导致此错误.但是由于在调试时无法捕获此错误,所以找不到原因.

 公共类ResponseWrapperFilter:IActionFilter,IOrderedFilter{私有MYContext myContext;私有IMemoryCache缓存;private只读TimeSpan _defaultCacheTimespan = new TimeSpan(0,5,0);私有void FillNavigationProperties(object v,int recursionLevel){如果(recursionLevel> 2)返回;如果(v.GetType().FullName =="System.DateTime" || v.GetType().FullName =="System.String";||v.GetType().FullName =="Newtonsoft.Json.Linq.JObject")返回;if(v!= null& v.GetType().FullName.StartsWith("System.Collections.Generic.List"))){foreach(var x in(IList)v){foreach(x.GetType().GetProperties()中的y){如果(x.GetType().FullName =="System.DateTime" || x.GetType().FullName =="System.String"||x.GetType().FullName =="Newtonsoft.Json.Linq.JObject")继续;var prop = y.GetGetMethod().Invoke(x,null);如果(prop!= null){FillNavigationProperties(prop,recursionLevel +1);}别的{TryToFillNavigationProperty(x,y);}}}}}私有无效的TryToFillNavigationProperty(对象v,PropertyInfo p){如果(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().其中​​(_ => _.Name == p.Name +"Id").First().GetGetMethod().Invoke(v,null);var cacheKey ="Company-> Id->"+ CompanyId.ToString();如果(cache.TryGetValue(cacheKey,out Company CompanyIdCompany)){p.GetSetMethod().Invoke(v,new [] {CompanyIdCompany});}别的{CompanyIdCompany = myContext.Set< Company>().其中​​(_ => _.Id == CompanyId).FirstOrDefault();cache.Set(cacheKey,CompanyIdCompany,_defaultCacheTimespan);** p.GetSetMethod().Invoke(v,new [] {CompanyIdCompany}); **}}}}公共无效OnActionExecuted(ActionExecutedContext上下文){如果(context.Exception为null){myContext = context.HttpContext.RequestServices.GetRequiredService< MYContext>();缓存= context.HttpContext.RequestServices.GetRequiredService< IMemoryCache>();如果(context.Result.GetType()== typeof(ObjectResult)){当前对象=(((ObjectResult)context.Result).Value;如果(当前!= null)FillNavigationProperties(current,0);context.Result =新的OkObjectResult(新的MYResponse(){数据=当前});}}}} 

据我从日志中看到的,粗线引起了错误(p.GetSetMethod().Invoke(v,new [] {CompanyIdCompany});)

解决方案

由于该问题仅偶尔显示,因此,该问题的可能原因是与请求操作并行运行的(偶然的)生成后台操作.此并行操作可以访问请求的 DbContext 实例.

注意:如果在一次成功的操作后错误仍然存​​在,则表明 DbContext 被其使用者之一俘虏;换句话说,就是圈养依赖性.

在大多数情况下,后台操作在请求结束之前完成,因此在处理 DbContext 之前完成.但是,在某些情况下,(偶然的)并行操作会有所延迟,导致它在请求结束之后(因此在之后)访问 DbContext > DbContext 由ASP.NET Core处置.

这可能是由于您忘记了 await 异步操作而发生的.如果您忘记 await 这样的操作,它将开始并行运行.但是,并行运行此类代码通常会出现问题,尤其是在处理诸如 DbContext 之类的对象时,因为它们不是线程安全的.

因此,分析堆栈跟踪以找到您忘记等待操作的位置.例如:

 公共异步任务DoSomethingAsync(){等待这个.this.DoSomethingElseAsync();//忘记等待}私有异步任务DoSomethingElseAsync(){//利用dbcontext等待this.dbContext.GetEntitiesAsync();} 

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

这篇关于Asp.net核心:无法访问已处置的对象.导致此错误的常见原因是处理从依赖项注入中解决的上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆