EF Competing SaveChanges()调用

Jef*_*sen 5 .net c# entity-framework transactions sqlbulkcopy

我正在建立一个批处理系统.批量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();
    });
}
Run Code Online (Sandbox Code Playgroud)

单元:

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();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我基本上被困在一块岩石和一块坚硬的地方之间.如果我离开该IsolationLevel集合ReadCommitted,我会EF INSERT在不同的语句之间出现死锁Tasks.

如果我设置IsolationLevelReadUncommitted(因为我没有做任何事情,我认为会很好SELECTs)我得到了DbConcurrencyExceptions.

我一直无法找到任何有关的好信息DbConcurrencyExceptions,Entity 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问题.

Jef*_*sen 3

这个问题在这里得到了很好的解释: 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]) 值 (@p0, @p1)

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

正如我们所看到的,生成的 SQL 不是使用“SELECT SCOPE_IDENTITY()”直接返回 SCOPE_IDENTITY(),而是使用 SCOPE_IDENTITY() 返回的值对 Id 列执行 SELECT。当表中的记录数量很大时,这会显着减慢插入速度。当表被分区时,问题会变得更糟。