我正在构建一个批处理系统。批量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
,我会在不同EF
的{{1}} INSERT
语句之间出现死锁。
如果我将Tasks
设置为IsolationLevel
(由于我没有做ReadUncommitted
,我认为这样会很好),我得到SELECTs
。
我一直无法找到有关DbConcurrencyExceptions
和DbConcurrencyExceptions
的任何有用信息,但我猜测Entity Framework
实际上导致ReadUncommitted
收到无效的“插入行” “信息。
更新
以下是有关在执行INSERTS时实际导致死锁问题的一些背景信息:
显然,几年前Linq To SQL问世以及Microsoft通过更改scope_identity()的选择方式来解决这个问题。当Entity Framework出现同样的问题时,不确定为什么他们的位置已经改变为SQL Server问题。
答案 0 :(得分: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。假设表是 订单和标识列是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
可以看到,而不是直接使用返回SCOPE_IDENTITY() 'SELECT SCOPE_IDENTITY()',生成的SQL执行SELECT Id列使用SCOPE_IDENTITY()返回的值。当。。。的时候 表中记录的数量很大,这显着减慢 插入。当表被分区时,问题就出现了 更糟糕的是。