如何在同时运行这两个语句时阻止EntityFramework死锁

时间:2012-10-27 04:13:58

标签: c# entity-framework deadlock

调用我的Web服务使用以下代码以确保调用者具有有效会话。如果找到有效会话,则会更新会话详细信息并保存更改。一切都很简单,工作正常。

// Create the Entity Framework context
using(MyContext ctx = CreateMyContext())
{
     // Get the user session for the client session         
     UserSession session = (from us in context.UserSessions.Include("UserEntity")
                            where us.SessionId = callerSessionId
                            select us).FirstOrDefault<UserSession>();

     if (session == null)
         return false;
     else
     {
         // Update session details
         session.Calls++;
         session.LastAccessed = DateTime.Now.Ticks;
         Console.WriteLine("Call by User:{0}", session.UserEntity.Name);

         // Save session changes back to the server
         ctx.SaveChanges();
         return true;
     }    
}

一切正常,直到同一个调用者,因此相同的会话,进行多个并发调用(这是完全有效的)。在这种情况下,我有时会遇到僵局。使用SQL Server Profiler我可以看到发生以下情况。

来电者A执行选择并获取用户会话的共享锁。呼叫者B执行选择并在同一用户会话上获取共享锁。由于来电者B的共享锁定,来电者A无法执行更新。由于呼叫者A的共享锁,呼叫者B无法执行其更新。死锁。

这看起来像一个简单而经典的死锁场景,必须有一个简单的方法来解决它。当然,几乎所有真实世界的应用程序都有同样的问题。但是我没有提到任何有关死锁的实体框架书籍。

3 个答案:

答案 0 :(得分:11)

我发现了一篇关于此HERE的文章。它基本上听起来像你可以启动和停止围绕你的EF调用的交易...该块给出了以下代码示例,因此信用转到Diego B Vega ...博客文章还链接到另一个博客并提供其他信息。

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
    TransactionOptions { IsolationLevel= IsolationLevel.Snapshot }))
{
    // do something with EF here
    scope.Complete();
}

答案 1 :(得分:1)

以下是否适合您?

using(MyContext ctx = CreateMyContext())
{

     ctx.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");


     // Get the user session for the client session         
     ...
}

答案 2 :(得分:0)

您可能每个会话都使用一个事务吗?在这种情况下,它们将死锁,因为它们在尝试保存时都会尝试从共享锁升级到排他锁。由于EF支持开放式并发,因此似乎没有充分的记录。

摆脱这种情况的一种方法是使用如下所示的内容提供更新锁提示:

return context.TestEntities
          .SqlQuery("SELECT TOP 1 Id, Value FROM TestEntities WITH (UPDLOCK)")
          .Single();
}

请参阅:entity framework 6 and pessimistic concurrency