如果我正在事务中进行大量更新,并且它抛出异常,我仍然想在同一数据库上下文中的表中记录一些信息,以便我知道发生了什么。我错误地认为,根据这个答案,我所需要做的就是创建一个新的 context
实例,并且这些数据库插入将与事务分开。然而,它们也正在被回滚。我不确定是否是因为我缺少
new Database()
包装器,但它不允许我创建此语句,因为 Database
类似乎没有允许这样做的公共构造函数。然后我看到了这个答案,这可能解释了原因。我可以看到您可能希望在事务中包含多个上下文。 那么我如何在事务中从该事务中排除一些数据库插入,这样它们就不会回滚?
这是一个过于简化的例子。实际上,
TransactionScope()
是在控制器的顶层创建的,并且
try{}
发生了许多深层方法调用。var contextA = new ContextA();
using (var scope = new TransactionScope())
{
// Log Errors in DB with new instance of same context
var contextAforLogging = new ContextA()
// Save entity in context A
contextA.Save(...);
//log step
contextAforLogging.Save(...);
contextAforLogging.SaveChanges();
// More updates to original entity in context A
contextA.Save(...);
//log step
contextAforLogging.Save(...);
contextAforLogging.SaveChanges();
// Commit tx-scope
scope.Complete();
}
catch (Exception ex) {
return....;
}
using var transaction = context1.BeginTransaction();
context2.Database.UseTransaction(transaction.GetDbTransaction());
如果“否”,那么您很可能不需要显式交易。 EF 已经将操作包装在事务中,并利用导航属性将相关实体作为聚合根插入,将看到自动设置的 FK 等内容。当调用
SaveChanges
时,任何完全独立的实体也将全部保存在事务范围内。
如果您确实需要TransactionScope
,那么您的日志记录代码可以选择退出事务范围。我建议使用 Log() 方法来封装它,这样就不会在某个地方遗漏它:
private void Log(string message)
{
using var txScope = new TransactionScope(TransactionScopeOption.Suppress));
using var contextForLogging = new AppDbContext(); // or better a DbContextFactory.Create()
//log step
var logEntry = new LogEntry(message);
contextForLogging.Logs.Add(logEntry);
contextforLogging.SaveChanges();
txScope.Commit();
}
理想情况下,当使用单个 DbContext 处理工作时,您应该避免多次调用
SaveChanges()
。这通常表明您忽略了导航属性并尝试手动管理实体之间的关系,而 EF 完全在幕后支持这种关系。