忽略特定查询的TransactionScope
本文关键字:TransactionScope 查询 | 更新日期: 2023-09-27 18:27:59
我正在寻找一种方法,在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
方法,我更愿意只在一个地方处理它。
如果在启用了抑制选项的情况下将日志调用封装在另一个事务范围内,则不会使用事务范围。
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;
}
}
这只是我最初的想法,但您需要将LogRepo放在它自己的DataContext(DC2)上,这样当它没有提交时,周围的TransactionScope(带有DC1)就不会回滚它。
基本上,您需要使您的日志记录独立且原子化。
EDIT在我看来,如果你把你的注销从SaveChanges移到DoSomething()上的catch()中,你的日志记录就会起作用。但是,您的日志记录仍然需要是独立的和原子的。
我发现了一个我不太满意但似乎有效的解决方案。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;
}
}