使用oracle的ManagedDataAccess.Client.OracleConnection处理由带有EntityFramework的mssql和Oracle(12c)支持的WebApi项目。我们使用autofac为每个请求注入一个上下文实例,但所有oracle访问都是临时完成的。
我们有某些操作同时依赖于两个数据库,因此我们选择使用TransactionScope对象来管理事务。
在大多数情况下,它运作良好,促进分布式的轻量级交易工作很好。但是在完成分布式事务后我遇到了一个问题。
假设:
public void Test()
{
var preItem = new HelpItem
{
Field1 = "pre batch";
};
_context.Items.Add(preItem);
_context.SaveChanges(); // This save always works.
var batchResult = FooService.BatchOperation(true);
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // This will succeed/fail depending on whether FooService caused a distributed transaction.
}
使用BatchOperation方法:
public Result BatchOperation(bool triggerDtc)
{
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
if (triggerDtc){
// Make requests to both databases.
} else {
// Make request to one database.
}
// Always complete for the sake of the demonstration.
transaction.Complete();
}
}
如果遇到分布式交易,然后完成&完全处置的EF似乎无法像交易发挥作用一样恢复并重新开始工作。
错误:
分布式事务已完成。要么以新的形式登记此会话 事务或NULL事务。
处理此问题的正确方法是什么?
对于这种特殊情况,您可以简单地围绕第二部分创建另一个事务:
var batchResult = FooService.BatchOperation(true);
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // This save depends on whether FooService caused a distributed transaction.
transaction.Complete();
}
但是这个问题出现了,因为FooService.BatchOperation方法只是对另一个数据库的查找而被改变,在不知不觉中打破了调用后继续使用上下文的每个方法。使用普通事务,可以自由地在其中使用单个EF上下文而不会出现问题,是否有任何方法可以实现与分布式事务相同的操作?
编辑: 这真让我困惑了。只是在另一个(非分布式)事务管理器中发出请求的行为足以恢复EF功能。
public IHttpActionResult Test()
{
var preItem = new HelpItem
{
Field1 = "pre batch";
};
_context.Items.Add(preItem);
_context.SaveChanges(); // This save works.
var batchResult = FooService.BatchOperation(true);
using (var transaction = new new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var lookupAnything = _context.Items.ToList();
transaction.Complete(); // This is optional, because we really don't care and it's disposed either way.
}
var postItem = new HelpItem
{
Field1 = "post batch";
};
_context.Items.Add(postItem);
_context.SaveChanges(); // Now this always works.
}
显然,我无法随处可见,所以仍然不确定实际解决方案是什么。