11

所以我正在研究这个实体框架项目,它将被用作一种 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;
            }
        }
    }
}

和:

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;
            }
        }
    }
}

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

我记得这里有人提到,如果我的上下文不共享,我就不会遇到这些死锁问题。

这对我来说似乎不是“共享的”,因为我在每种方法中都使用了 new BackEndEntities(),那么我必须进行哪些更改才能使其更健壮?

此 DAL 将用于在 Internet 上公开的 Web 服务(经过代码审查之后),因此我无法控制它的压力程度,并且许多不同的实例可能想要更新同一个实体。

谢谢!

4

3 回答 3

9

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();
}

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

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

于 2012-09-03T08:30:25.737 回答
6

在大型系统中,死锁自由是一个相当困难的问题。它本身与EF无关。

缩短事务的生命周期可以减少死锁,但会引入数据不一致。在您之前陷入僵局的那些地方,您现在正在破坏数据(没有任何通知)。

所以根据逻辑事务选择你的上下文生命周期和你的事务生命周期,而不是根据物理考虑。

打开快照隔离。这完全排除了阅读交易的问题。

要编写事务,您需要找到锁排序。通常它是悲观地锁定在更高级别的最简单方法。示例:您是否总是在客户的上下文中修改数据?将该客户的更新锁定作为交易的第一条语句。这通过序列化对该客户的访问来提供完全的死锁自由度。

于 2012-08-28T20:36:57.847 回答
2

上下文赋予实体与数据库对话的能力,没有上下文就没有什么去哪里的概念。因此,启动上下文是一件大事,它占用了大量资源,包括数据库等外部资源。我相信您的问题是“新”命令,因为您将有多个线程试图启动并获取相同的数据库资源,这肯定会死锁。

您发布的代码似乎是反模式。从外观上看,您的实体上下文会相对快速地旋转并超出范围,而您的存储库 CRUD 对象似乎会持续更长的时间。

我为其实现实体的公司的方式传统上完全相反 - 只要程序集需要数据库,就会创建并保留上下文,并且在微秒内创建存储库 CRUD 对象并消亡。

我不能说你从哪里得到你的上下文没有被共享的断言,所以我不知道是在什么情况下说的,但你不应该在程序集之间共享上下文是绝对正确的。 在同一个程序集中,我看不出有什么理由不知道启动上下文需要多少资源,以及启动上下文需要多长时间。实体上下文非常繁重,如果您要通过单线程使当前代码工作,我怀疑您会看到一些绝对糟糕的性能。

因此,我建议改为重构它,以便您拥有Create(BackEndEntites context)and Update(BackEndEntities context),然后让您的主线程(创建所有这些子线程的那个)创建并维护一个 BackEndEntities 上下文以传递给它的子线程。AgentController还要确保在使用完s 和s 后立即摆脱OrderController它们,并且永远不要在方法之外重用它们。实现一个好的控制反转框架,如 Ninject 或 StructureMap 可以使这变得容易得多。

于 2012-08-28T20:30:28.777 回答