为什么在第一个请求完成之前,此TransactionScope不会阻止后续请求?

时间:2014-10-13 13:32:47

标签: c# asp.net-mvc entity-framework entity-framework-5 transactionscope

我现在一直在调查交易两天,也许在收到这么多信息之后我遗漏了一些明显的东西。这里的目标是阻止同时请求。如果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

这是我对代码应该如何工作的理解:

显然它不像我想要的那样工作。是否可以使用TransactionScope实现我的目标?或者我必须升级到EF 6吗?

1 个答案:

答案 0 :(得分:2)

序列化无法解决旧的“检查然后插入”问题。两个可序列化的事务可以同时评估条件,得出它们必须插入的结论,然后两个尝试仅插入一个失败而一个插入成功。这是因为所有读取在可序列化隔离下彼此兼容(实际上它们在所有隔离级别下兼容)。

有许多思想流派如何解决这个问题。有人建议使用MERGE。有些人建议在检查查询中使用锁定提示来获取X或U锁定。我个人建议始终INSERT并优雅地恢复重复密钥违规。 工作的另一种方法是使用显式app locks

EF或System.Transactions确实只添加了他提出的噪音。这基本上是一个后端SQL问题。至于如何在线程之间流动事务范围的问题,请参阅Get TransactionScope to work with async / await(显然,你已经知道这一点,从读取OP ...我没有在第一次读取时注册)。你需要这个来让你的异步代码在正确的上下文中登记,但阻塞/锁定仍然是基本的后端问题。