10

对我的 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 无法执行其更新。僵局。

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

4

3 回答 3

15

我在这里找到了一篇讨论这个的文章。基本上听起来您可以启动和停止围绕您的 EF 调用的事务...该块提供了以下代码示例,因此归功于 Diego B Vega... 该博客文章还链接到另一个包含其他信息的博客。

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
    TransactionOptions { IsolationLevel= IsolationLevel.Snapshot }))
{
    // do something with EF here
    scope.Complete();
}
于 2012-10-27T04:34:28.957 回答
1

以下内容对您有用吗?

using(MyContext ctx = CreateMyContext())
{

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


     // Get the user session for the client session         
     ...
}
于 2013-08-15T23:56:23.620 回答
0

您可能每个会话都使用事务?在这种情况下,它们将死锁,因为事务在尝试保存时都尝试从共享锁升级到独占锁。这似乎没有很好的记录,因为 EF 支持乐观并发。

解决此问题的一种方法是使用以下内容提供更新锁提示:

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

参见:实体框架 6 和悲观并发

于 2019-07-20T13:11:56.060 回答