2011-10-07 114 views
5

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

我所做的解决此问题的方法是设置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,则会在EFINSERT之间出现不同Tasks之间的死锁。

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

我一直无法找到有关DbConcurrencyExceptionsEntity Framework什么好的信息,但我猜测,ReadUncommitted基本上造成EF接收信息无效“插入行”。

UPDATE

这里是什么是真正引起我的死锁问题,同时执行插入一些背景资料:

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

显然,同样的问题在几年前是存在,当Linq到SQL出来了,微软通过改变如何选择scope_identity()来修复它。不知道为什么当Entity Framework出现同样的问题时,他们的立场就变成了SQL Server的问题。

+0

_competing_或_completing_? –

回答

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到SQL生成用于插入极其低效 SQL到这样的表。假设表格是 顺序,同一列是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

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