在使用 Autofac 管理范围的每个请求后正确调用 EF SaveChanges

properly call EF SaveChanges after each request with Autofac managing scope

我想在每个请求结束时为我的项目 SaveChanges 添加一些基础设施到我的数据库上下文中。

所以我创建了一个简单的 Owin 中间件

app.Use(async (ctx, req) => {
     await req();
     var db = DependencyResolver.Current.GetService<MyDbContext>();
     await db.SaveChangesAsync();
});

这不起作用并抛出错误

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

如果我在完成请求之前解析数据库

app.Use(async (ctx, req) => {
     var db = DependencyResolver.Current.GetService<MyDbContext>();
     await req();
     await db.SaveChangesAsync();
});

它不会抛出错误,但它也不起作用(因为更改未保存到数据库中,在调试器中查看数据库显示 DbSetLocal 属性 抛出一个关于它被处理的 InvalidOperationException

我试过使用和不使用异步,在 autofac 配置之前和之后注册中间件 (app.UseAutofacMiddleware(container)) 并直接从 Owin 环境解析 LifetimeScope。都给我相同的结果。

我以前用 Structuremap 做过类似的事情,但似乎无法找到让 Autofac 正常运行的正确方法。

史蒂文关于您不应该提交请求处理这一事实是正确的,因为您无法确定您是否真的想在那里提交,除非您从 DbContext 中抽象出您的 UoW 并在那里保留一个成功属性,检查是否关于处置和有条件地提交。

对于您的具体问题,有两点需要澄清。

  1. DbContext 或 UoW 需要注册到 InstancePerRequest()
  2. 你应该使用 OnRelease(context => context.SaveMyChangesIfEverythingIsOk()) native Autofac API
  3. 而不是使用 Owin 中间件

例如,这就是 RavenDb 的样子

   builder.Register(x =>
        {
            var session = x.Resolve<IDocumentStore>().OpenAsyncSession();
            session.Advanced.UseOptimisticConcurrency = true;
            return session;
        })
            .As<IAsyncDocumentSession>()
            .InstancePerRequest()
            .OnRelease(x =>
            {
                x.SaveChangesAsync();
                x.Dispose();
            });