【问题标题】:SaveChangesAsync does not work in receive block in AKKA.NETSaveChangesAsync 在 AKKA.NET 的接收块中不起作用
【发布时间】:2016-09-07 07:13:53
【问题描述】:

我在使用 AKKA.NET 接收函数中的 SaveChangesAsync 将更改保存到我的数据库时遇到问题。有人可以解释发生了什么吗?

更多细节:我有这个接收块:

Receive<UpdateUnitVM>((msg) => {
                using (var scope = AutofacDependencyContainer.Current.CreateTenantScope(new TenantId(msg.UnitConfiguration.TenantId)))
                {
                    var db = scope.GetService<ApplicationDbContext>();

                    ...work on some objects inside the db context

                    //now save and continue with next actor using PipeTo
                    var continueFlags = TaskContinuationOptions.AttachedToParent & TaskContinuationOptions.ExecuteSynchronously;
                    db.SaveChangesAsync().ContinueWith(myint => new UnitConditionRuleGuard.UnitChanged(msg.UnitConfiguration.GetTenantUnitPair()), continueFlags).PipeTo(unitConditionRuleGuard);
                }
            });

如果我将代码更改为像这样使用 SaveChanges,它会起作用:

Receive<UpdateUnitVM>((msg) => {
                using (var scope = AutofacDependencyContainer.Current.CreateTenantScope(new TenantId(msg.UnitConfiguration.TenantId)))
                {
                    var db = scope.GetService<ApplicationDbContext>();

                    ...work on some objects inside the db context

                    //now save (blocking) and continue with next actor sequentially
                    db.SaveChanges();
                    unitConditionRuleGuard.Tell(new UnitConditionRuleGuard.UnitChanged(msg.UnitConfiguration.GetTenantUnitPair()));
                }
            });

注意 using-block 创建了一个新的 Autofac 依赖注入容器作用域,因此在 using 块退出后,db-object 被释放。我有一种感觉,这就是问题所在。但是,我不确定如何处理它以及如何适当地延长 dbcontext-object 的生命周期。

【问题讨论】:

  • 不确定它是否适合您的场景,但您是否尝试过 ReceiveAsync() 和 await db.SaveChanges()

标签: .net akka akka.net actor-model


【解决方案1】:

使用SaveChangesAsync + ContinueWith 与等待保存更改完成不同。您在那里所做的是使用后续操作进行异步调用,然后在完成保存更改之前处理数据库上下文。

您可以通过使用ReceiveAsync 处理程序并使用await db.SaveChangesAsync(); 来完成,或者通过将using { .. } 块扔掉并显式处理范围,而不是在ContinueWith 块内,您已经在开始 - 第二个选项风险更大,因为 Actor 在处理下一条消息之前不会等待操作完成,并且可能会在结果中创建多个范围。

【讨论】:

    【解决方案2】:

    好吧,我不会使用 DI 容器。 :)

    另一种选择是手动管理数据库上下文,而不是“使用(var thing)” - 这可以通过打开连接,完成工作,然后在异步保存后继续,关闭连接,然后 PipeTo 到别的地方。

    【讨论】:

    • 我还没准备好扔掉我的 DI 容器,因为它给了我其他好处:-) 但是,我以稍微不同的方式遵循了你的建议,仍然使用 DI 容器。通过删除 using-block 并添加此任务继续:...ContinueWith(x => scope.Dispose())。 DI 范围将清理和处置 DbContext。现在它起作用了。但是,我担心如果抛出异常导致我们永远无法到达最后一个 ContinueWith-block 会发生什么。
    • 不会在延续中捕获到异常吗?可以检查任务是否出错
    【解决方案3】:

    我最终得到了这个可行的解决方案,它看起来相当不错,而且仍然相当简单。我唯一担心的是,如果在其中一个延续中引发异常,它将如何表现,因此将不胜感激进一步的 cmets。

    Receive<UpdateUnitVM>((msg) => {
                    var scope = AutofacDependencyContainer.Current.CreateTenantScope(new TenantId(msg.UnitConfiguration.TenantId));
    
                    var db = scope.GetService<ApplicationDbContext>();
    
                    ...work on some objects inside the db context...
    
                    //now save and continue with next actor using PipeTo
                    var continueFlags = TaskContinuationOptions.AttachedToParent & TaskContinuationOptions.ExecuteSynchronously;
                    db.SaveChangesAsync()
                       .ContinueWith(...transform message..., continueFlags)
                       .PipeTo(...some other actor...)
                       .ContinueWith(x => scope.Dispose(), continueFlags);
                }
            });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-10
      • 2016-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多