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 ObjectContex
t 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?
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:
Add
, Attach
, Find
, Local
ou Remove
sur DbSet
GetValidationErrors
, Entry
ou SaveChanges
sur DbContext
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 =.
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
Dans .netcore 2.0, cela a été déplacé vers:
context.ChangeTracker.AutoDetectChangesEnabled = false;
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.