我正在寻找一种在TransactionScope处于活动状态时执行查询的方法,并忽略TransactionScope - 基本上,我想执行此特定查询,无论如何。
我首先使用EF代码,并且设计应用程序的方式,在一次调用中多次打开一个新的数据上下文,每个都有自己的更改,所有这些都包含在一个TransactionScope中,假设没有失败,最后调用Complete()
。在上下文中,我们覆盖了SaveChanges
,以便在base.SaveChanges()
上发生任何异常时,我们可以在回滚事务之前捕获它并登录到数据库。
由于SaveChanges
发生在事务内部,因此显然不会发生日志记录,因为它属于与原始调用相同的事务。我试图完全忽略TransactionScope的日志代码。
这是一些精简代码:
// From the context
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
// Writes to the log table - I want this to run no matter what
LogRepo.Log(/*stuff to log from the context*/);
throw;
}
}
// Inside the business logic
public void DoSomething() {
try {
using (var scope = new TransactionScope()) {
using (var context = new FooContext()) {
// Do something
context.SaveChanges();
}
using (var context = new FooContext()) {
// Do something else
context.SaveChanges();
}
scope.Complete();
}
} catch (Exception ex) {
// scope.Complete is never called, so the transaction is rolled back
}
}
我尝试使用常规ADO.NET而不是EF来进行日志记录,但结果仍然相同 - 它也会回滚。
我需要在SaveChanges
内部进行错误处理,因为我正在记录的是正在保存的实体的状态 - 所以我不能轻易地将记录移动到其他地方。我可以在SaveChanges catch
内部构建消息,并抛出它并让DoSomething catch
记录它,但是有几十个DoSomething
方法,而我更倾向于处理这个问题。一个地方。
答案 0 :(得分:35)
如果在启用了suppress选项的情况下将日志调用包装在另一个事务范围内,则不会使用事务范围。
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
using (var scope = new TransactionScope(TransactionScopeOption.Suppress)) {
LogRepo.Log(message); // stuff to log from the context
}
throw;
}
}
答案 1 :(得分:1)
只是我最初的想法,但你需要将你的LogRepo放在它自己的DataContext(DC2)上,以便周围的TransactionScope(带有DC1)在没有提交时不会回滚。
基本上,您需要使您的日志记录自包含且原子化。
编辑在查看它时,在我看来,如果你将Logging从SaveChanges移到DoSomething()上的catch(),你的日志记录就可以了。但是,您的日志记录仍然需要自包含和原子。
答案 2 :(得分:1)
我发现了一个我不满意的解决方案,但似乎有效。 TransactionScope显然只影响当前线程,因此使用新线程进行日志记录似乎没有问题。
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (Exception ex) {
string message = /*stuff to log from the context*/;
new Thread(msg => {
LogRepo.Log(msg);
}).Start(message);
throw;
}
}