设想
理想情况下使用 DI ( Ninject ) 来管理范围内的上下文/存储库,但实际上仅限于服务定位器模式,不要问。理想的例子:
// EF code-first context kernel.Bind<MyDbContext>().ToSelf().InRequestScope().WithConstructorArgument("connectionString",...); // EF code-first repositories kernel.Bind<IRepository<SomeModel>>().To<EfRepository<SomeModel, MyDbContext>>().InRequestScope(); kernel.Bind<IRepository<AuditLog>>().To<EfRepository<AuditLog, MyDbContext>>().InRequestScope();
在我的服务调用中,我尝试将错误数据保存到数据库中,因此 repo/context
.SaveChanges()
正确失败:try { var model = new SomeModel { Name = ..., Blah = ... } repoSomeModels.Add(model); repoSomeModels.Commit(); // bad data, throws exception }
然后,我想将这次失败的尝试记录到同一数据库中:
catch(Exception ex) { repoAuditLogs.Add(new AuditLog { Action = ..., Result = ex.Message }); repoAuditLogs.Commit(); throw ex; // rethrow the problem so we can properly bail }
但是,当尝试在第二次提交 ( repoAuditLogs.Commit()
) 中保存更改时,我再次遇到相同的异常。这是因为第一次尝试提交的“坏数据”仍然存在于 EF 上下文中,即使尝试失败,所以 EF 再次尝试提交坏数据,但不知道它不应该这样做。
问题
如何从 EF 上下文中删除失败的更改?还是我要解决这个问题?
(更新:不要专注于我的场景的细节,即因为我有效地记录了一个错误,所以使用不同的上下文可能是合适的,但假设我只想做其他事情)
可能的解决方案
我遇到了这篇博文——http: //rundevrun.blogspot.com/2012/06/entity-framework-removing-failed.html——它说你应该找到所有有问题的实体并将它们的更改标记为“已接受” ,这导致 EF 在后续提交期间“忽略”它们。这适用于我的情况,允许我保存我的审计日志并“退出”。
然而,一位同事指出,这会导致上下文不再与其所代表的数据正确同步,因此是一个糟糕的主意——即使对我的情况来说“没问题”(因为我要立即停止程序)如果您要继续请求(如原始帖子所建议的那样),您将冒着尝试使用“损坏”上下文的风险,并且可能会产生意想不到的副作用。
在解决死锁的另一种情况下,他将提交显式包装在(基于代码的)事务中(EF 已经在 SQL 中执行此操作),因此他可以重新尝试它,但这并不能解决我的问题,即数据错误,而不是连接。
我能想到的唯一其他解决方案是将我的审计保存在一个新的上下文中。理想情况下,我使用 DI/locator 将我的审计 repo 映射到不同的上下文,例如(强调new MyDbContext
或应该是什么):
kernel.Bind<IRepository<AuditLog>>().To<EfRepository<AuditLog, MyDbContext>>(new MyDbContext(...)).InRequestScope();
但是后来我有两个上下文浮动,这似乎与首先共享上下文的观点相反。