我有一个代码将数据添加到两个EntityFramework 6 DataContexts,如下所示:
using(var scope = new TransactionScope())
{
using(var requestsCtx = new RequestsContext())
{
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
logsCtx.SaveChanges();
}
}
}
Requests
表中有一个插入触发器,它使用RAISEERROR
拒绝某些值。这种情况很正常,应该由调用try-catch
方法的SaveChanges
块处理。但是,如果第二个SaveChanges
方法失败,则必须完全还原对两个DataContexts的更改 - 因此是事务范围。
出现错误:当requestsCtx.SaveChanges()
抛出异常时,整个Transaction.Current
的状态设置为Aborted
,后者logsCtx.SaveChanges()
失败并显示以下内容:
TransactionException:
The operation is not valid for the state of the transaction.
为什么会发生这种情况?如何告诉EF第一个例外并不重要?
答案 0 :(得分:0)
真的不确定这是否有效,但可能值得尝试。
private void SaveChanges()
{
using(var scope = new TransactionScope())
{
var log = CreateRequest();
bool saveLogSuccess = CreateLogEntry(log);
if (saveLogSuccess)
{
scope.Complete();
}
}
}
private LogEntry CreateRequest()
{
var req = new Request { Id = 1, Value = 2 };
var log = new LogEntry { RequestId = 1, State = "OK" };
using(var requestsCtx = new RequestsContext())
{
requestsCtx.Requests.Add(req);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
finally
{
return log;
}
}
}
private bool CreateLogEntry(LogEntry log)
{
using(var logsCtx = new LogsContext())
{
try
{
logsCtx.Logs.Add(log);
logsCtx.SaveChanges();
}
catch (Exception)
{
return false;
}
return true;
}
}
来自transactionscope的文档:http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx
如果事务范围内没有异常(即介于两者之间) TransactionScope对象的初始化和调用 它的Dispose方法),然后是范围内的事务 参与者被允许继续。如果在其中发生异常 交易范围,它参与的交易将 回滚。
基本上只要遇到异常,就会回滚事务(因为您似乎已经意识到了) - 我认为可能会工作,但我真的不确定并且可以&#39 ; t测试确认。这似乎违背了交易范围的预期用途,而且我对异常处理/冒泡不够熟悉,但也许它会有所帮助! :)
答案 1 :(得分:0)
我想我终于明白了。诀窍是为第一个SaveChanges
使用隔离的事务:
using(var requestsCtx = new RequestsContext())
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
using(var outerScope = new TransactionScope())
{
using(var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
requestsCtx.SaveChanges();
innerScope.Complete();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
}
logsCtx.SaveChanges();
outerScope.Complete();
}
}
警告:由于性能原因,大多数关于RequiresNew
模式的文章都不鼓励使用它。它适用于我的场景,但是如果有任何我不知道的副作用,请告诉我。