为什么会出现错误:使用EF和AutoFac在.net core 2中“无法访问已处理的对象”?

epi*_*tka 2 entity-framework autofac asp.net-core

首先是错误:

Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and
Run Code Online (Sandbox Code Playgroud)

然后稍后尝试在应用程序的其他位置使用相同的上下文实例。如果在上下文上调用Dispose()或将上下文包装在using语句中,则可能会发生这种情况。如果使用依赖项注入,则应让依赖项注入容器负责处理上下文实例。对象名称:“ MemberContext”。

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

域有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();

        }
    }
Run Code Online (Sandbox Code Playgroud)

在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
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

        }
Run Code Online (Sandbox Code Playgroud)

这是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"
            });
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

我想做的是删除一条记录。在客户端代码看起来像这样

 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();
            });
        }
    }
Run Code Online (Sandbox Code Playgroud)

此删除请求被映射到将其传递给中介的控制器

控制者

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

            _mediator.Send(command).ConfigureAwait(false);
        }
Run Code Online (Sandbox Code Playgroud)

最后是处理程序

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

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

看到此注释掉的成员检查是否为null。那也引发了错误,我注释掉它只是为了看看会发生什么,现在它以SaveChangesAsync的形式抛出。

epi*_*tka 5

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

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

   await _mediator.Send(command).ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)