【问题标题】:Why am I getting error: "Cannot access disposed object" in .net core 2 with EF and AutoFac?为什么我在 .net core 2 中使用 EF 和 AutoFac 出现错误:“无法访问已处理的对象”?
【发布时间】:2018-02-12 13:46:14
【问题描述】:

首先是错误:

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

然后尝试在您的其他地方使用相同的上下文实例 应用。如果您在 上下文,或将上下文包装在 using 语句中。如果你是 使用依赖注入,你应该让依赖注入 容器负责处理上下文实例。 对象名称:'MemberContext'。

我有 3 个项目,域、API 和 WebSPA 应用程序。

Domain 有 2 个模块,DomainModule 和 MediatorModule

 public class DomainModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(typeof(MemberContext).Assembly)
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope(); // via assembly scan

            builder.RegisterType<MemberContext>().AsSelf()
                    .InstancePerLifetimeScope();          // or individually
        }
    }

 public class MediatorModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // enables contravariant Resolve() for interfaces with single contravariant ("in") arg
            builder
                .RegisterSource(new ContravariantRegistrationSource());

            // mediator itself
            builder
                .RegisterType<Mediator>()
                .As<IMediator>()
                .InstancePerLifetimeScope();

            // request handlers
            builder
                .Register<SingleInstanceFactory>(ctx =>
                {
                    var c = ctx.Resolve<IComponentContext>();
                    return t =>
                    {
                        object o;
                        return c.TryResolve(t, out o) ? o : null;
                    };
                })
                .InstancePerLifetimeScope();

            // notification handlers
            builder
                .Register<MultiInstanceFactory>(ctx =>
                {
                    var c = ctx.Resolve<IComponentContext>();
                    return t => (IEnumerable<object>) c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
                })
                .InstancePerLifetimeScope();

        }
    }

在 API 项目中,我还有 2 个模块,ApplicationModule 和 MediatorModule 与上述相同。

 public class ApplicationModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
                        .AsImplementedInterfaces()
                        .InstancePerLifetimeScope(); // via assembly scan

            builder.RegisterType<MemberContext>().AsSelf().InstancePerLifetimeScope();          // or individually
        }
    }

不,当我调试时,我可以看到成员上下文在每次请求时都会更新,但在第二次请求时,它会抛出上述错误。为了确保我不会发疯,我修改了 dbcontext 的构造函数来为上下文创建一个 id,这样我就可以验证它们是否不同。我做错了什么?

 public MemberContext(DbContextOptions<MemberContext> options) : base(options)
        {
            MemberContextId = Guid.NewGuid();

            Console.WriteLine("member context created: " + MemberContextId);

        }

这里是 API 中的启动

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                //    .AllowCredentials()
            );
        });

        services.AddMvc()
                .AddControllersAsServices();//Injecting Controllers themselves thru DI
                        //For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services

        AddSwaggerGen(services);

        //var connection = Configuration["ConnectionString"];

        //services.AddDbContext<MemberContext>(options => options.UseSqlServer(connection),ServiceLifetime.Scoped);


        services.AddEntityFrameworkSqlServer()
            .AddDbContext<MemberContext>(options =>
                {
                    options.UseSqlServer(Configuration["ConnectionString"]
                        //,sqlServerOptionsAction: sqlOptions =>
                        //{
                        //    sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
                        //    sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
                        //}
                        );
                },
                ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
            );


        var container = new ContainerBuilder();
        container.Populate(services);

        container.RegisterAssemblyModules(typeof(VIN.Members.Domain.Entities.Member).Assembly,
                                          typeof(Startup).Assembly);


        return new AutofacServiceProvider(container.Build());
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        //NOTE: must be before UseMVC !!!
        app.UseCors("CorsPolicy");
        app.UseMvc();

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        });
    }

    private void AddSwaggerGen(IServiceCollection services)
    {
        services.AddSwaggerGen(options =>
        {
            options.DescribeAllEnumsAsStrings();
            options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
            {
                Title = "VIN Members HTTP API",
                Version = "v1",
                Description = "Members Service HTTP API",
                TermsOfService = "Terms Of Service"
            });
        });
    }
}

更新:

我想要做的是删除一条记录。客户端代码如下所示

 onDelete(item: IMember) {
        //TODO: replace this with dialog service component
        if (window.confirm('Are sure you want to delete this member?')) {
            //put your delete method logic here
            this.service.deleteMember(item).subscribe(x => {

                this.getMembers();
            });
        }
    }

这个删除请求被映射到一个控制器,该控制器将它传递给中介

控制器

  // DELETE api/members/5
        [HttpDelete("{id}")]
        public void Delete(Guid id)
        {
            var command = new DeleteMember.Command(id);

            _mediator.Send(command).ConfigureAwait(false);
        }

最后是处理程序

public class DeleteMember
{
    public class Command : IRequest
    {
        public Command(Guid memberId)
        {
            Guard.NotNull(memberId, nameof(memberId));

            MemberId = memberId;
        }

        public Guid MemberId { get; }

    }

    public class Handler : AsyncRequestHandler<Command>
    {
        private MemberContext _context;

        public Handler(MemberContext context)
        {
            _context = context;
            Console.WriteLine("Delete member context: " + context.MemberContextId);
        }

        protected override async Task HandleCore(Command cmd)
        {
            try
            {
                var member = await _context.FindAsync<Member>(cmd.MemberId);//.ConfigureAwait(false);

               // if (member != null)
               //// {
                    _context.Remove(member);

                    await _context.SaveChangesAsync().ConfigureAwait(false);
               // }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

        }
    }
}

如您所见,没有代码可以处理该上下文。挠头。

如果为 null,请查看此已注释掉的成员检查。这也引发了错误,我将其注释掉只是为了看看会发生什么,现在它作为 SaveChangesAsync 引发。

【问题讨论】:

    标签: entity-framework asp.net-core autofac


    【解决方案1】:

    随着请求的完成,上下文被释放。由于命令处理程序使用 SaveChangesAsync(),因此在保存完成之前释放上下文。罪魁祸首是控制器方法:)。它也应该是异步的。

    [HttpDelete("{id}")]
    public async Task Delete(Guid id)
    {
        var command = new DeleteMember.Command(id);
    
       await _mediator.Send(command).ConfigureAwait(false);
    }
    

    【讨论】:

      【解决方案2】:

      您的 DbContext 是作用域的,这意味着每次在同一个 HTTP 请求中请求一个对象时,依赖注入将返回相同的 DbContext 对象(在 ASP.NET 的情况下)。

      这意味着你不应该在你的DbContext 上调用Dispose(否则不能再次使用同一个对象)。无论有意还是无意,这似乎就是发生在你身上的事情。

      这确实意味着您不应该使用using。您是否在代码中的任何位置使用using 来对抗您的DbContext

      我认为您没有显示抛出异常的行。

      更新: 尝试在 MemberContext 类中覆盖 Dispose。像这样的:

      public override void Dispose() {
          base.Dispose();
      }
      

      但只要在那里设置一个断点。当它中断时(如果确实如此)检查堆栈跟踪并查看调用它的内容。

      【讨论】:

      • 我很难过。也许是Autofac?我没有使用 Autofac 的经验。我更新了我的答案,提出了一个追捕它的建议。
      • 嗯,没有可覆盖的 Dispose 方法,但我在我的 dbContext 上实现了 IDisposable 并且没有调用它。
      • 这很奇怪。 DbContext 实现了IDisposible,所以它应该在那里....哦!看来我跟你说的有误。我更新了Dispose 方法的代码。对不起。这次我实际测试了它:)
      • 这太疯狂了。第一个请求,新上下文,作为依赖项传入,运行查询,被释放。下一个请求,新的上下文再次被实例化并作为依赖传递,但是当访问它时它说它已被释放,即使它还没有被释放。啊啊啊啊啊
      • 我读了一些关于 Autofac 的文章。我最好的猜测是你必须调整你的RegisterType 行。
      【解决方案3】:

      根据我的经验,这也可能是由于 WebAPI 中有 async void 而不是 async Task 造成的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-19
        • 2018-07-16
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多