EF Competing SaveChanges()调用

.net c# entity-framework sqlbulkcopy transactions

我正在建立一个批处理系统。批量Units的数量从20到1000。每个Unit基本上是模型的层次结构(一个主模型和许多子模型)。我的任务涉及将每个模型层次结构作为单个事务保存到数据库(每个层次结构提交或回滚)。不幸的是, EF无法处理模型层次结构的两个部分,因为它们可能包含数千条记录。

我为解决这个问题所做的工作是设置SqlBulkCopy来处理这两个可能很高的计数模型,并让EF处理其余的插入(以及参照完整性)。

批量循环:

foreach (var unitDetails in BatchUnits)
{
  var unitOfWork = new Unit(unitDetails);
  Task.Factory.StartNew(() =>
    {
      unitOfWork.ProcessX(); // data preparation
      unitOfWork.ProcessY(); // data preparation
      unitOfWork.PersistCase();
    });
}

单元:

class Unit
{
  public PersistCase()
  {
    using (var dbContext = new CustomDbContext())
    {
      // Need an explicit transaction so that 
      // EF + SqlBulkCopy act as a single block
      using (var scope = new TransactionScope(TransactionScopeOption.Required,
        new TransactionOptions() {
          IsolationLevel = System.Transaction.IsolationLevel.ReadCommitted
        }))
      {
        // Let EF Insert most of the records
        // Note Insert is all it is doing, no update or delete
        dbContext.Units.Add(thisUnit);
        dbContext.SaveChanges();  // deadlocks, DbConcurrencyExceptions here

        // Copy Auto Inc Generated Id (set by EF) to DataTables
        // for referential integrity of SqlBulkCopy inserts
        CopyGeneratedId(thisUnit.AutoIncrementedId, dataTables);

        // Execute SqlBulkCopy for potentially numerous model #1
        SqlBulkCopy bulkCopy1 = new SqlBulkCopy(...);
        ...
        bulkCopy1.WriteToServer(dataTables["#1"]);

        // Execute SqlBulkCopy for potentially number model #2
        SqlBulkCopy bulkCopy2 = new SqlBulkCopy(...);
        ...
        bulkCopy2.WriteToServer(dataTables["#2"]);

        // Commit transaction
        scope.Complete();
      }
    }
  }
}

现在我基本上被困在一块岩石和一块坚硬的地方之间。如果我将IsolationLevel设置为ReadCommitted ,我会在不同的Tasks EF INSERT语句之间出现死锁。

如果我将IsolationLevel设置为ReadUncommitted (我认为这样就好了,因为我没有做任何SELECTs ),我得到了DbConcurrencyExceptions

我一直无法找到关于DbConcurrencyExceptionsEntity Framework任何好信息,但我猜测ReadUncommitted本质上是导致EF接收无效的“插入行”信息。

UPDATE

以下是有关在执行INSERTS时实际导致我的死锁问题的一些背景信息:

http://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005

显然,几年前Linq To SQL出现时微软通过改变scope_identity()的选择方式来解决这个问题。当Entity Framework出现同样的问题时,不确定为什么他们的位置已经改变为这是一个SQL Server问题。

一般承认的答案

这个问题在这里得到了很好的解释: http//connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server- 2005年

基本上它是一个内部EF问题。我迁移了我的代码以使用Linq To SQL,它现在工作正常(不再对标识值使用不必要的SELECT )。

来自Linq To Sql中完全相同的问题的相关引用已修复:

当表具有标识列时,Linq to SQL会生成效率极低的SQL,以便插入到此类表中。假设该表是Order,而identiy列是Id。生成的SQL是:

exec sp_executesql N'INSERT INTO [dbo]。[Order]([Colum1],[Column2])VALUES(@ p0,@ p1)

SELECT [t0]。[Id] FROM [dbo]。[Order] AS [t0] WHERE [t0]。[Id] =(SCOPE_IDENTITY())',N'@ p0 int,@ p1 int,@ p0 = 124 ,@ P1 = 432

通过使用'SELECT SCOPE_IDENTITY()'可以看到,而不是直接返回SCOPE_IDENTITY(),生成的SQL使用SCOPE_IDENTITY()返回的值在Id列上执行SELECT。当表中的记录数量很大时,这会显着减慢插入速度。当表被分区时,问题变得更糟。




许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因