Pour votre remarque dans les commentaires à votre question:
"...SavingChanges (pour chaque
enregistrer)..."
C'est la pire chose que vous pouvez faire! Appelant SaveChanges()
pour chaque enregistrement ralentit insertions extrêmement bas. Je ferais quelques tests simples qui seront très probablement d'améliorer les performances:
- Appelez
SaveChanges()
une fois après TOUS les records.
- Appelez
SaveChanges()
après par exemple 100 enregistrements.
- Appelez
SaveChanges()
après par exemple 100 enregistrements et jetez-le contexte et en créer un nouveau.
- Désactiver la détection de changement
Pour des insertions je suis en train de travailler et d'expérimenter avec un modèle comme ceci:
using (TransactionScope scope = new TransactionScope())
{
MyDbContext context = null;
try
{
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
{
++count;
context = AddToContext(context, entityToInsert, count, 100, true);
}
context.SaveChanges();
}
finally
{
if (context != null)
context.Dispose();
}
scope.Complete();
}
private MyDbContext AddToContext(MyDbContext context,
Entity entity, int count, int commitCount, bool recreateContext)
{
context.Set<Entity>().Add(entity);
if (count % commitCount == 0)
{
context.SaveChanges();
if (recreateContext)
{
context.Dispose();
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
}
}
return context;
}
J'ai un programme de test qui insère 560.000 entités (9 propriétés scalaires, pas de propriétés de navigation) dans la DB. Avec ce code, il fonctionne en moins de 3 minutes.
Pour la performance, il est important d'appeler SaveChanges()
après avoir "beaucoup" des dossiers ("beaucoup" autour de 100 ou 1000). Il améliore également les performances de disposer du contexte après SaveChanges et en créer un nouveau. Cette opération efface le contexte de toutes les entités, SaveChanges
ne le fait pas, les entités sont toujours attachées dans le contexte de l'état d' Unchanged
. C'est l'augmentation de la taille des joints des entités dans le contexte de ce qui ralentit l'insertion étape par étape. Donc, il est utile de le vider après un certain temps.
Voici quelques mesures pour mon 560.000 entités:
- commitCount = 1, recreateContext = false: nombre d'heures (C'est votre procédure actuelle)
- commitCount = 100, recreateContext = false: plus de 20 minutes
- commitCount = 1000, recreateContext = false: 242 sec
- commitCount = 10000, recreateContext = false: 202 sec
- commitCount = 100000, recreateContext = false: 199 sec
- commitCount = 1000000, recreateContext = false: de mémoire d'exception
- commitCount = 1, recreateContext = true: plus de 10 minutes
- commitCount = 10, recreateContext = true: 241 sec
- commitCount = 100, recreateContext = true: 164 sec
- commitCount = 1000, recreateContext = true: 191 sec
Le comportement dans le premier test ci-dessus est que la performance est très non-linéaire et diminue très au fil du temps. ("Nombre d'heures" est une estimation, je n'ai jamais terminé ce test, je me suis arrêté à 50.000 entités au bout de 20 minutes.) Cette non-linéarité n'est pas si importante dans tous les autres tests.