我现在一直在调查交易两天,也许在收到这么多信息之后我遗漏了一些明显的东西。这里的目标是阻止同时请求。如果condition
为真,则插入数据,之后condition
将为false。同时请求将在插入数据之前检查condition
,然后两者都会尝试插入数据。
public async Task<ActionResult> Foo(Guid ID)
{
Debug.WriteLine("entering transaction scope");
using (var transaction = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable },
TransactionScopeAsyncFlowOption.Enabled
))
{
Debug.WriteLine("entered transaction scope");
var context = new DbContext();
Debug.WriteLine("querying");
var foo = context.Foos.FirstOrDefault(/* condition */);
Debug.WriteLine("done querying");
context.Foos.Add(new Foo());
/* async work here */
Debug.WriteLine("saving");
context.SaveChanges();
Debug.WriteLine("saved");
Debug.WriteLine("exiting transaction scope");
transaction.Complete();
Debug.WriteLine("exited transaction scope");
return View();
}
}
这是使用Fiddler一次执行两个请求时的调试输出:
entering transaction scope entered transaction scope querying done querying entering transaction scope entered transaction scope querying done querying saving saving saved A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll exiting transaction scope exited transaction scope
这是我对代码应该如何工作的理解:
DbContext.Database.Connection.BeginTransaction
,在执行查询时会抛出错误:
当分配给命令的连接处于挂起的本地事务中时,ExecuteReader要求命令具有事务 该命令的Transaction属性尚未初始化。
TransactionScope
通常会导致原因问题。
http://entityframework.codeplex.com/discussions/429215
但是,.NET 4.5.1添加了额外的构造函数来处理这个问题。
How to dispose TransactionScope in cancelable async/await? 显然它不像我想要的那样工作。是否可以使用TransactionScope
实现我的目标?或者我必须升级到EF 6吗?
答案 0 :(得分:2)
序列化无法解决旧的“检查然后插入”问题。两个可序列化的事务可以同时评估条件,得出它们必须插入的结论,然后两个尝试仅插入一个失败而一个插入成功。这是因为所有读取在可序列化隔离下彼此兼容(实际上它们在所有隔离级别下兼容)。
有许多思想流派如何解决这个问题。有人建议使用MERGE。有些人建议在检查查询中使用锁定提示来获取X或U锁定。我个人建议始终INSERT并优雅地恢复重复密钥违规。 工作的另一种方法是使用显式app locks。
EF或System.Transactions确实只添加了他提出的噪音。这基本上是一个后端SQL问题。至于如何在线程之间流动事务范围的问题,请参阅Get TransactionScope to work with async / await(显然,你已经知道这一点,从读取OP ...我没有在第一次读取时注册)。你需要这个来让你的异步代码在正确的上下文中登记,但阻塞/锁定仍然是基本的后端问题。