实体框架中的死锁

时间:2017-12-06 16:14:00

标签: c# sql-server entity-framework transactions

我有一个几乎同时收到几个请求的Web服务。

如果发生这种情况,两个请求的事务处理死锁,我必须回滚它们的重新运行。

        using (var context = new DbContext())
        using (var dbContextTransaction = context.Database.BeginTransaction(IsolationLevel.Serializable))
        {
                var subscription = context.Subscriptions.FirstOrDefault(x => x.Provider == provider);
                if (subscription == null)
                {
                   */Insert*/
                }
                else
                {
                   /*Update*/
                }
                context.SaveInTransaction(null);
                dbContextTransaction.Commit();
                }

据我所知,两个请求都进入了交易并阅读了订阅表。在准备提交时,它们会死锁,因为它们无法在另一个事务(IsolationLevel.Serializable)读取的数据范围中插入值。

如果我没有使TransactionLevel Serializeable,其中一个请求无法满足提供者列的唯一约束并获得回滚。

我该怎么做才能防止死锁?

1 个答案:

答案 0 :(得分:2)

  
    

如果我没有使TransactionLevel Serializeable,其中一个请求无法满足提供者列的唯一约束并获得Rollbacked。

  

你可以:

1)处理PK违规/死锁并继续更新现有行。请注意,您应该使用READ COMMITTED并处理PK违规,因为您知道发生故障的会话可以继续更新。使用SERIALIZABLE,您可以在INSERT或UPDATE上获得死锁。

2)使用带有锁定提示的原始SQL使一个会话等待另一个会话。

正确的锁定提示是(UPDLOCK,SERIALIZABLE)。如果您使用MERGE,则锁定提示仍然需要 。见https://blogs.msdn.microsoft.com/dbrowne/2013/02/25/why-is-tsql-merge-failing-with-a-primary-key-violation-isnt-it-atomic/

3)在事务开始时调用sp_getapplock以确保只有一个会话具有锁定。请注意,CLR锁定仅在您拥有应用程序的单个实例时才有效。对于客户端/服务器应用程序或Web场,CLR锁定将不起作用。

您可以在DbContext中添加一个方法,例如:

public void GetAppLock(string lockName)
{
    this.Database.ExecuteSqlCommand("exec sp_getapplock @name, 'exclusive'", new System.Data.SqlClient.SqlParameter("@name",lockName));
}