Estoy construyendo un sistema de procesamiento por lotes. Los lotes de Units
vienen en cantidades de 20-1000. Cada Unit
es esencialmente una jerarquía de modelos (un modelo principal y muchos modelos secundarios). Mi tarea consiste en guardar cada jerarquía del modelo en una base de datos como una transacción única (ya sea que cada jerarquía se comprometa o se retrotraiga). Desafortunadamente EF
no pudo manejar dos porciones de la jerarquía del modelo debido a su potencial para contener miles de registros.FE Competing SaveChanges() Llamadas
Lo que he hecho para resolver esto está configurado en SqlBulkCopy
para manejar estos dos modelos de conteo potencialmente alto y dejar que EF
maneje el resto de las inserciones (y la integridad referencial).
lotes Loop:
foreach (var unitDetails in BatchUnits)
{
var unitOfWork = new Unit(unitDetails);
Task.Factory.StartNew(() =>
{
unitOfWork.ProcessX(); // data preparation
unitOfWork.ProcessY(); // data preparation
unitOfWork.PersistCase();
});
}
Unidad:
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();
}
}
}
}
En este momento estoy esencialmente atrapado entre la espada y la pared. Si dejo el IsolationLevel
configurado en ReadCommitted
, obtengo interbloqueos entre EF
INSERT
declaraciones en Tasks
diferentes.
Si fijo el IsolationLevel
-ReadUncommitted
(que pensé que estaría bien ya que no estoy haciendo ninguna SELECTs
) consigo DbConcurrencyExceptions
.
no he podido encontrar ninguna buena información sobre DbConcurrencyExceptions
y Entity Framework
pero supongo que ReadUncommitted
está esencialmente causando EF
de recibir "filas insertadas" no válidos información.
ACTUALIZACIÓN
Aquí hay alguna información básica sobre lo que realmente está causando mi deadlocking problemas mientras se hacen inserciones:
Al parecer, este mismo problema estaba presente hace unos años, cuando LINQ to SQL salió y Microsoft lo solucionó cambiando la forma en que se selecciona scope_identity(). No estoy seguro de por qué su posición ha cambiado a ser un problema de SQL Server cuando surgió el mismo problema con Entity Framework.
_competing_ o _completing_? –