Sto costruendo un sistema di elaborazione batch. I lotti di Units
sono disponibili in quantità da 20 a 1000. Ogni Unit
è essenzialmente una gerarchia di modelli (un modello principale e molti modelli figlio). Il mio compito consiste nel salvare ogni gerarchia di un modello in un database come una singola transazione (o si impegna ciascuna gerarchia o si esegue il rollback). Sfortunatamente, EF
non è stato in grado di gestire due parti della gerarchia del modello a causa della possibilità di contenere migliaia di record.EF Competing SaveChanges() Chiamate
Quello che ho fatto per risolvere questo problema è impostare SqlBulkCopy
per gestire questi due modelli di conteggio potenzialmente elevato e lasciare che EF
gestisca il resto degli inserti (e integrità referenziale).
Batch loop:
foreach (var unitDetails in BatchUnits)
{
var unitOfWork = new Unit(unitDetails);
Task.Factory.StartNew(() =>
{
unitOfWork.ProcessX(); // data preparation
unitOfWork.ProcessY(); // data preparation
unitOfWork.PersistCase();
});
}
Unità:
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();
}
}
}
}
In questo momento sono sostanzialmente bloccato tra l'incudine e il martello. Se lascio il IsolationLevel
impostato su ReadCommitted
, ottengo deadlock tra EF
INSERT
dichiarazioni in diversi Tasks
.
Se ho impostato il IsolationLevel
-ReadUncommitted
(che ho pensato che sarebbe bene visto che non sto facendo alcuna SELECTs
) ottengo DbConcurrencyExceptions
.
Sono stato in grado di trovare alcun bene informazioni su DbConcurrencyExceptions
e Entity Framework
ma credo che ReadUncommitted
sostanza, causando EF
per ricevere "righe inserite" non valide informazioni.
UPDATE
Ecco alcune informazioni su ciò che è effettivamente causando la mia deadlocking problemi mentre si fa INSERTI:
A quanto pare questo stesso problema era presente a pochi anni fa, quando LINQ to SQL è uscito e Microsoft lo ha risolto cambiando il modo in cui scope_identity() viene selezionato. Non sono sicuro del motivo per cui la loro posizione è cambiata a questo problema di SQL Server quando lo stesso problema si è verificato con Entity Framework.
_completo_ o _completo_? –