799 votes

Moyen le plus rapide d’insérer dans Entity Framework

Je cherche le moyen le plus rapide de l’insertion dans Entity Framework, je vous demande cela à cause du scénario où vous avez un TransactionScope active et l’insertion est énorme (4000 +), il peut potentiellement durer plus de 10 minutes (délai d’attente par défaut des transactions), et vous serez dirigé vers une transaction incomplète.

Cordialement.

1114voto

Slauma Points 76561

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.

191voto

arkhivania Points 731

Cette combinaison augmenter vitesse assez bien.

93voto

Adam Rackis Points 45559

Vous devriez regarder à l’aide de la `` pour cela. Voici la documentation, et bien sûr il y a beaucoup de tutoriels en ligne.

Désolé, je sais que vous cherchiez une réponse simple à obtenir EF de faire ce que vous voulez, mais les opérations en bloc ne sont pas vraiment ce que ORMs sont destinés.

87voto

maxlego Points 1945

Le moyen le plus rapide serait d’utiliser bulk insert extension

Il utilise SqlBulkCopy et datareader personnalisé pour obtenir le rendement maximum. En conséquence, c’est plus de 20 fois plus rapide qu’avec un insert régulier ou AddRangeEntityFramework.BulkInsert vs EF AddRange

son utilisation est extrêmement simple

56voto

Irfons Points 171

Je suis d’accord avec Adam Rackis. ``est le moyen le plus rapide de transfert de dossiers en vrac d’une source de données à l’autre. J’ai utilisé ce copier 20K records, et il a fallu moins de 3 secondes. Jetez un oeil à l’exemple ci-dessous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X