交易死锁,如何正确设计?

Fra*_*rme 9 .net c# multithreading deadlock entity-framework-4.1

所以我正在研究这个实体框架项目,它将被用作DAL的类型,并且在运行压力测试时(通过Thread()开始对实体进行一些更新)并且我得到了这些:

_innerException = {"事务(进程ID 94)在锁资源上与另一个进程死锁,并被选为死锁牺牲品.重新运行事务."}

这是我如何实现类的方法的一些示例:

public class OrderController
{

    public Order Select(long orderID)
    {
        using (var ctx = new BackEndEntities())
        {

            try
            {
                var res = from n in ctx.Orders
                                       .Include("OrderedServices.Professional")
                                       .Include("Agency")
                                       .Include("Agent")
                          where n.OrderID == orderID
                          select n;
                return res.FirstOrDefault();
            }
            catch (Exception ex)
            {
                throw ex;
            }
         }
    }

    public bool Update(Order order)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                order.ModificationDate = DateTime.Now;
                ctx.Orders.Attach(order);
                ctx.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和:

public class AgentController
{

    public Agent Select(long agentID)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                var res = from n in ctx.Agents.Include("Orders")
                          where n.AgentID == agentID
                          select n;
                return res.FirstOrDefault();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }

    public bool Update(Agent agent)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                agent.ModificationDate = DateTime.Now;
                ctx.Agents.Attach(agent);
                ctx.ObjectStateManager.ChangeObjectState(agent, System.Data.EntityState.Modified);
                ctx.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,这里的代码可能会更好,但我更喜欢EF新手.但我认为我的问题是上下文的设计问题.

我记得有人在这里提到如果我的上下文没有被共享,我将不会遇到这些死锁问题.

当我在每个方法中使用新的BackEndEntities()时,这对我来说似乎并不"共享" ,那么我需要更改哪些内容才能使其更加健壮?

此DAL将用于在互联网上公开的Web服务(在coure的代码审查之后),因此我无法控制它将被强调多少以及许多不同的实例可能想要更新同一实体.

谢谢!

Hen*_*bæk 8

thouse死锁的原因不是您的代码,而是由于EF使用SERIALIZABLE作为默认的TransactionScope隔离级别.

SERIALIZABLE是最受限制的锁定,这意味着您默认情况下选择了最严格的隔离级别,并且您可以期待大量锁定!

解决方案是根据您要执行的操作指定另一个TransactionScope.您可以使用以下内容围绕EF操作:

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

阅读有关此问题的更多信息

http://blogs.msdn.com/b/diego/archive/2012/04/01/tips-to-avoid-deadlocks-in-entity-framework-applications.aspx

http://blogs.u2u.be/diederik/post/2010/06/29/Transactions-and-Connections-in-Entity-Framework-40.aspx

http://blog.aggregatedintelligence.com/2012/04/sql-server-transaction-isolation-and.html

https://serverfault.com/questions/319373/sql-deadlocking-and-timing-out-almost-constantly


usr*_*usr 6

在大系统中,死锁自由是一个非常难的问题.它本身与EF无关.

缩短事务的生命周期可以减少死锁,但会导致数据不一致.在那些你以前陷入僵局的地方,你现在正在销毁数据(没有任何通知).

因此,请根据逻辑事务选择上下文生命周期和事务生存期,而不是根据物理因素.

打开快照隔离.这使得阅读交易完全脱离了等式.

要编写事务,您需要找到锁定顺序.通常情况下,它是悲观锁定和更高级别的最简单方法.示例:您是否始终在客户的上下文中修改数据?将该客户的更新锁定作为您的交易的第一个声明.这通过序列化对该客户的访问来提供完全的死锁自由.