我在使用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对象的生命周期。
答案 0 :(得分:3)
使用SaveChangesAsync
+ ContinueWith
与等待保存更改完成不同。你在那里做的是与后续操作进行异步调用,然后在完成之前处理数据库上下文以保存更改。
您可以使用ReceiveAsync
处理程序并使用await db.SaveChangesAsync();
来完成修复,或者通过抛出using { .. }
块并在ContinueWith
内明确处置范围来解决问题块,你在开头写的 - 第二个选项风险更大,因为actor在处理下一条消息之前不会等待操作完成,并且可能会在结果中创建多个范围。
答案 1 :(得分:1)
好吧,我不会使用DI容器。 :)
一种替代方法是手动管理数据库上下文,而不是使用(var thing)&#39; - 这可以通过打开连接,完成工作,然后在异步保存后继续,关闭连接,然后PipeTo到其他地方。
答案 2 :(得分:0)
我最终得到了这个工作解决方案,看起来相当不错,但仍然相当简单。我唯一关心的是,如果在其中一个延续中抛出异常,它将如何表现,以便进一步评论。
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);
}
});