实体框架代码中的死锁第一版v 4.3

时间:2013-04-02 20:52:28

标签: .net entity-framework entity-framework-4

我有一个数据库,我通过EF代码第一个模型更新。这个数据库包含一个父母"桌子和10或12岁儿童"表。父表和子表之间存在外键关系。

最初,当我想在子表中创建记录时,我会使用EF创建父行,获取主键,并使用EF创建子行,使用主键作为外键在孩子身上

最近我重新考虑了我的模型以使用TPT继承。所以现在我有一个基类(对应于我的基表)和子类(对应于我的子表)。所以现在如果我想创建一个子记录,我创建一个子类型的对象,填充所有必需的字段并使用上下文保存。我假设(因为它是合乎逻辑的)EF生成的SQL与我手动执行的操作完全相同:

  1. 创建父记录。
  2. 获取新父记录的主键
  3. 创建子记录
  4. 现在的问题是,使用TPT继承,我遇到了死锁错误。每天5-10次尝试在此数据库中创建条目时,我将遇到死锁错误。这个数据库是一个网站的日志数据库,所以我想说90%的操作都是写操作。我用它来记录网站上的活动。

    如果我在网站的测试实例上加载了足够的负载,我可以轻松地使错误发生。我正在尝试从我的DBA获取SQL跟踪,因为我自己没有能力(即使在测试中)。我知道这将是最有帮助的,当我得到它时,我会在这里发布,但我的第一个问题是,是否有任何明显的我做错了?我在网上研究了这个,试图找出EF使用的并发类型,并且响应是混合的。有人说它使用SQL服务器默认值(读取已提交),而其他人说由于它在TransactionScope对象中包装了它的查询,因此它使用默认的TransactionScope隔离级别,即Serializable。

    有人可以确定使用了什么隔离级别,和/或在创建属于TPT继承树的实体时生成了什么SQL?

    感谢。

    修改 因此,为了回应@ usr对我原始帖子中的评论的建议,我尝试使用ReadCommitted级别将我的保存包装在事务范围内,就像这样

    TransactionOptions options = new TransactionOptions();
    options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
    using (TransactionScope scope = new     TransactionScope(TransactionScopeOption.RequiresNew, options))
    {
        retVal = AuditEventContext.SaveChanges();
        scope.Complete();
    }
    

    我仍然遇到相同的死锁错误。我正在从DBA获取SQL跟踪。

    为了在这个应用程序中添加更多上下文,我将添加我的上下文(上面的代码中的AuditEventContext)正被MVC应用程序使用。每个控制器都获得它自己的AuditEventContext实例,因此我在多个线程中有多个上下文来更新同一个数据库。我确定这是造成僵局的原因,我只是不明白为什么,特别是因为我的原始代码模仿我假设EF正在为基于继承的插入做的事情。

    编辑#2以回答以下问题

    还有其他应用程序可以读取此数据,但是当我知道没有任何内容正在读取时,我可以发生此错误。我一下子就把50个左右的用户扔到我的网站上,这几乎就是马上发生的。幸运的是,我们同时在我们的网站上没有50个用户,但是:)我们没有插入很多行。这是一个审计数据库,因此我们编写"审计事件"整个网站,即页面请求,接受法律条款,注销等。因此插入非常小。上下文存在于控制器的生命周期中,因此可以使用相同的上下文来插入多个事件,但每个事务都很小。我希望我们不生成重复键,因为父表上的键是自动生成的整数标识。这是SQL服务器管理它的工作。父表上有4个索引,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON。我没有设置这些,我只是留下了SQL默认值。索引也有IGNORE_DUP_KEY = OFF

    @RuneG,上下文由Unity生成(依赖注入)。我没有直接创建它。我正在调用Dispose,但是,当调用拥有上下文的控制器时,我调用Dispose,而不是在调用save更改之后立即调用,就像我使用using语句创建上下文一样。这会引发问题吗?使用相同的上下文进行多次更新?

    编辑#3 - 死锁SQL

    最后获得了一条SQL跟踪。这是一个死锁SQL的例子

    exec sp_executesql N'insert [dbo].[PFIAccessLog]([AuditEventID], [Screen],     [PolicyNumber], [CoverageSequence], [PersonTypeID])
    values (@0, @1, @2, @3, @4)
    select [PFIAccessLogID]
    from [dbo].[PFIAccessLog]
    where @@ROWCOUNT > 0 and [AuditEventID] = @0',N'@0 bigint,@1 nvarchar(100),@2   nvarchar(50),@3 nvarchar(50),@4    int',@0=2035631,@1=N'ContractList',@2=N'220001197',@3=N'1',@4=1
    

    exec sp_executesql N'insert [dbo].[PFIAccessLog]([AuditEventID], [Screen],     [PolicyNumber], [CoverageSequence], [PersonTypeID])
    values (@0, @1, @2, @3, @4)
    select [PFIAccessLogID]
    from [dbo].[PFIAccessLog]
    where @@ROWCOUNT > 0 and [AuditEventID] = @0',N'@0 bigint,@1 nvarchar(100),@2     nvarchar(50),@3 nvarchar(50),@4     int',@0=2035630,@1=N'ContractList',@2=N'220001197',@3=N'1',@4=1
    

    PFIAccessLog是我的TPT层次结构中的子表之一。

    编辑4

    好的,现在我的代码就是这样,我仍然会遇到死锁:

    TransactionOptions options = new TransactionOptions();
    options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
    using (TransactionScope scope = new     TransactionScope(TransactionScopeOption.RequiresNew, options))
    {
        context.Database.Connection.Open();
        context.Database.Connection.EnlistTransaction(Transaction.Current);
        retVal = context.SaveChanges();
        scope.Complete();
    }
    

    我还在代码中进行了更改,以便在每次编写时创建新的上下文,而不是在MVC控制器的生命周期内共享上下文(可能是多次写入)。

    编辑#5

    好的,根据@RuneG的建议,我将隔离级别设置为读取未提交,并修复了问题。但是,我对此有点紧张。如果你看一下死锁的示例语句,我回读的部分内容就是主键值。如果我可以从另一个事务中读取未提交的数据,并且这些密钥是由数据库自动生成的,那么当我回读时,是否有可能获得一些其他记录ID?

    假设这些SQL片段中的每一个都在一个事务中执行,并且因为它们都是1次写入和1次读取到同一个表,我假设死锁发生在这样的

    交易1写入了它的数据。它获取并释放独占锁 事务1获取读取的共享锁。 事务2为其插入获得写锁定。

    Bam,读取死锁,对吗?事务1无法继续,因为存在写锁定。事务2不能继续,因为有一个共享锁来防止事务2写入数据事务1可能看起来很脏。

    所以假设我现在没有提交读书。

    交易1写入了它的数据。 事务1读取它的数据 交易2写入了它的数据 交易2读取它的数据。

    其中一个进程可以使用独占锁来阻止另一个进程,但不会获得任何共享锁。所以我关心的是主键的生成如何适应所有这些。我假设它是在桌面上有一个独占锁的情况下生成的,因此我的密钥没有被混淆的风险"?如果其他进程可能会读取最终被回滚的脏数据,我真的不在乎。钥匙是我的关注点。

    此外,这是多个线程运行相同类型的SQL语句的结果。线程是我的MVC应用程序中的请求。这似乎是一个标准的场景。我假设我的代码线程是安全的(即一次只有代码中的一个线程),我可以使用read committed吗?

    很抱歉这么冗长。我倾向于在解决问题时:)

0 个答案:

没有答案