web-dev-qa-db-fra.com

Pourquoi l'insertion d'entités dans EF 4.1 est-elle si lente par rapport à ObjectContext?

Fondamentalement, j'insère 35000 objets dans une transaction:

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

Cela prend une éternité! Si j'utilise le ObjectContext sous-jacent (en utilisant IObjectAdapter), il est toujours lent mais prend environ 20s. On dirait DbSet<> effectue des recherches linéaires, ce qui prend du temps ...

Quelqu'un d'autre voit ce problème?

79
Hartmut

Comme déjà indiqué par Ladislav dans le commentaire, vous devez désactiver la détection automatique des modifications pour améliorer les performances:

context.Configuration.AutoDetectChangesEnabled = false;

Cette détection des modifications est activée par défaut dans l'API DbContext.

La raison pour laquelle DbContext se comporte si différemment de l'API ObjectContext est que beaucoup plus de fonctions de l'API DbContext appellent DetectChanges en interne que les fonctions de ObjectContext API lorsque la détection automatique des modifications est activée.

Ici vous pouvez trouver une liste de ces fonctions qui appellent DetectChanges par défaut. Elles sont:

  • Les membres Add, Attach, Find, Local ou Remove sur DbSet
  • Les membres GetValidationErrors, Entry ou SaveChanges sur DbContext
  • La méthode Entries sur DbChangeTracker

Surtout Add appelle DetectChanges qui est responsable des mauvaises performances que vous avez connues.

Je contraste avec cela, l'API ObjectContext appelle DetectChanges uniquement automatiquement dans SaveChanges mais pas dans AddObject et les autres méthodes correspondantes mentionnées ci-dessus. C'est la raison pour laquelle les performances par défaut de ObjectContext sont plus rapides.

Pourquoi ont-ils introduit cette détection automatique des modifications par défaut dans DbContext dans tant de fonctions? Je ne suis pas sûr, mais il semble que le désactiver et appeler manuellement DetectChanges aux points appropriés est considéré comme avancé et peut facilement introduire des bogues subtils dans votre application, alors utilisez-le avec précaution =.

124
Slauma

Petit test empirique avec EF 4.3 CodeFirst:

Suppression de 1000 objets avec AutoDetectChanges = true: 23 sec

Suppression de 1000 objets avec AutoDetectChanges = false: 11 sec

1000 objets insérés avec AutoDetectChanges = true: 21 sec

1000 objets insérés avec AutoDetectChanges = false: 13 sec

11
Zax

Dans .netcore 2.0, cela a été déplacé vers:

context.ChangeTracker.AutoDetectChangesEnabled = false;

4
Maxvt

Outre les réponses que vous avez trouvées ici. Il est important de savoir qu'au niveau de la base de données, c'est plus de travail à insérer qu'à ajouter. La base de données doit étendre/allouer un nouvel espace. Ensuite, il doit mettre à jour au moins l'index de clé primaire. Bien que les index puissent également être mis à jour lors de la mise à jour, c'est beaucoup moins courant. S'il existe des clés étrangères, il doit également lire ces index pour s'assurer que l'intégrité référentielle est maintenue. Les déclencheurs peuvent également jouer un rôle bien que ceux-ci puissent affecter les mises à jour de la même manière.

Tout ce travail de base de données a du sens dans l'activité d'insertion quotidienne provoquée par des entrées utilisateur. Mais si vous téléchargez simplement une base de données existante, ou si vous avez un processus qui génère beaucoup d'insertions. Vous voudrez peut-être chercher des moyens d'accélérer cela en le reportant à la fin. Normalement, la désactivation des index lors de l'insertion est une méthode courante. Il existe des optimisations très complexes qui peuvent être faites selon les cas, elles peuvent être un peu écrasantes.

Sachez simplement qu'en général, l'insertion prendra plus de temps que les mises à jour.

1
Arturo Hernandez