33 votes

Pourquoi DbSet.Add fonctionne-t-il si lentement ?

Le même sujet a été abordé ici il y a 8 mois : Comment accélérer DbSet.Add() ? . Aucune solution n'a été proposée à part l'utilisation de SqlBulkCopy qui n'est pas acceptable pour nous. J'ai décidé d'en parler une fois de plus en espérant qu'il y ait de nouvelles pensées et idées autour de ce problème et que d'autres solutions de contournement soient proposées. En tout cas, je suis simplement curieux de savoir pourquoi cette opération prend autant de temps à s'exécuter.

Le problème est le suivant : je dois mettre à jour 30K entités dans une base de données (EF 4.1, POCO). Le type d'entité est assez simple, contenant un Id entier + 4 autres propriétés entières, sans relation avec d'autres types. 2 cas :

  • tous sont de nouveaux enregistrements. Exécuter context.Entities.Add(entity) un par un pour chaque entité prend 90 secondes avec Cntx.Configuration.AutoDetectChangesEnabled=false (la valeur vraie le fait tourner indéfiniment). Ensuite, SaveChanges ne prend qu'une seconde. Autre approche : l'attacher au contexte comme ceci prend les mêmes 90 secondes :

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Added;
  • tous sont des enregistrements existants avec quelques modifications. Dans ce cas, il ne faut que quelques millisecondes pour l'attacher à un contexte de données existant comme ceci :

    Cntx.Entities.Attach(entity);
    Cntx.Entry(entity).State = EntityState.Modified;

    Vous voyez la différence ?

Qu'est-ce qui se passe derrière la scène de la méthode Add et qui fait qu'elle fonctionne si incroyablement lentement ?

27voto

YMC Points 1255

J'ai des résultats intéressants de tests de performance et j'ai trouvé un coupable. Je n'ai vu aucune information de ce type dans aucune source de FE que j'ai lue.

Il s'avère que Equals est surchargé dans une classe de base. La classe de base est censée contenir la propriété Id partagée entre tous les types d'entités concrètes. Cette approche est recommandée par de nombreux ouvrages sur EF et est assez bien connue. Vous pouvez la trouver ici par exemple : Comment mettre en œuvre au mieux les égalités pour les types personnalisés ?

Plus exactement, les performances sont réduites par l'opération d'unboxing (conversion d'un objet en type concret) qui a rendu le fonctionnement si lent. Lorsque j'ai commenté cette ligne de code, il a fallu 3 secondes pour l'exécuter, contre 90 secondes auparavant !

public override bool Equals ( object obj )
{
    // This line of code made the code so slow 
    var entityBase = obj as EntityBase;
    ...
}

Lorsque je l'ai trouvé, j'ai commencé à réfléchir à ce qui pourrait être une alternative à cet Equals. La première idée était d'implémenter IEquatable pour EntityBase, mais il s'est avéré qu'il n'était pas exécuté du tout. J'ai donc décidé d'implémenter IEquatable pour chaque classe d'entité concrète de mon modèle. Je n'en ai que quelques unes, donc c'est une mise à jour mineure pour moi. Vous pouvez mettre toute la fonctionnalité de l'opération Equal (généralement, il s'agit de la comparaison de 2 ID d'objets) dans une méthode d'extension à partager entre les classes d'entités concrètes et l'exécuter comme ceci : Equal((EntityBase)ConcreteEntityClass). Le plus intéressant, c'est que cet IEquatable accélère 6 fois EntitySet.Add !

Je n'ai donc plus de problème de performance, le même code s'exécute pour moi en moins d'une seconde. J'ai obtenu un gain de performance de 180 fois ! Incroyable !

Conclusion :

  1. la façon la plus rapide d'exécuter EntitySet.Add est d'avoir IEquatable pour l'entité spécifique (0.5 sec)
  2. L'absence d'IEquatable le fait tourner 3 secondes.
  3. Le fait d'avoir Equals(object obj), ce que la plupart des sources recommandent, le fait fonctionner 90 secondes.

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