为什么我会收到错误:"无法访问已处置的对象"在.net core 2中使用EF和AutoFac?

时间:2018-02-12 13:46:14

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

首先是错误:

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

然后尝试在你的其他地方使用相同的上下文实例   应用。如果您正在调用Dispose(),则可能会发生这种情况   上下文,或将上下文包装在using语句中。如果你是   使用依赖注入,你应该让依赖注入   容器负责处理上下文实例。       对象名称:' MemberContext'。

我有3个项目,Domain,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。

3 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:0)

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

这意味着您不应该在DbContext上调用using(否则同一个对象无法再次使用)。这似乎是你有意或无意发生的事情。

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

我不认为你展示了抛出异常的行。

更新的 尝试覆盖MemberContext课程中的public override void Dispose() { base.Dispose(); } 。像这样:

ReactNative

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

答案 2 :(得分:0)

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