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.
如果我设置IsolationLevel为ReadUncommitted(因为我没有做任何事情,我认为会很好SELECTs)我得到了DbConcurrencyExceptions.
我一直无法找到任何有关的好信息DbConcurrencyExceptions,Entity Framework但我猜这ReadUncommitted实际上是导致EF收到无效的"行插入"信息.
UPDATE
以下是有关在执行INSERTS时实际导致死锁问题的一些背景信息:
显然,几年前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]) 值 (@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。当表中的记录数量很大时,这会显着减慢插入速度。当表被分区时,问题会变得更糟。
| 归档时间: |
|
| 查看次数: |
1625 次 |
| 最近记录: |