Entity Framework est trop lent. Quelles sont mes options?

J’ai suivi le mantra “Don’t Optimize Prematurely” et codé mon service WCF en utilisant Entity Framework.

Cependant, j’ai décrit les performances et Entity Framework est trop lent. (Mon application traite 2 messages en environ 1,2 seconde, lorsque l’application (héritée) que je réécris effectue 5 à 6 messages simultanément) (l’application héritée appelle les sprocs pour son access DB).

Mon profilage pointe vers Entity Framework en prenant la majeure partie du temps par message.

Alors, quelles sont mes options?

  • Y a-t-il de meilleurs ORM?
    (Quelque chose qui ne supporte que la lecture et l’écriture normales des objects et le fait rapidement ..)

  • Existe-t-il un moyen de rendre Entity Framework plus rapide?
    ( Note : quand je dis plus vite je veux dire sur le long terme, pas le premier appel. (Le premier appel est lent (15 secondes pour un message), mais ce n’est pas un problème. J’ai juste besoin que ce soit rapide pour le rest des messages.)

  • Une 3ème option mystérieuse qui m’aidera à accélérer mon service.

REMARQUE: La plupart de mes interactions avec la firebase database sont Create et Update. Je fais très très peu de sélection et de suppression.

Vous devez commencer par profiler les commandes SQL réellement émises par Entity Framework. Selon votre configuration (POCO, Entités auto-suivies), les optimisations sont nombreuses. Vous pouvez déboguer les commandes SQL (qui ne doivent pas différer entre le mode de débogage et le mode de publication) en utilisant la méthode ObjectSet.ToTraceSsortingng() . Si vous rencontrez une requête nécessitant une optimisation supplémentaire, vous pouvez utiliser certaines projections pour donner à EF plus d’informations sur ce que vous essayez d’accomplir.

Exemple:

 Product product = db.Products.SingleOrDefault(p => p.Id == 10); // executes SELECT * FROM Products WHERE Id = 10 ProductDto dto = new ProductDto(); foreach (Category category in product.Categories) // executes SELECT * FROM Categories WHERE ProductId = 10 { dto.Categories.Add(new CategoryDto { Name = category.Name }); } 

Pourrait être remplacé par:

 var query = from p in db.Products where p.Id == 10 select new { p.Name, Categories = from c in p.Categories select c.Name }; ProductDto dto = new ProductDto(); foreach (var categoryName in query.Single().Categories) // Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId { dto.Categories.Add(new CategoryDto { Name = categoryName }); } 

Je viens juste de le taper de ma tête, donc ce n’est pas exactement comment il serait exécuté, mais EF fait de belles optimisations si vous lui dites tout ce que vous savez sur la requête (dans ce cas, nous aurons besoin de la catégorie- des noms). Mais ce n’est pas comme le chargement rapide (db.Products.Include (“Categories”)) car les projections peuvent réduire davantage la quantité de données à charger.

Le fait est que les produits tels que Entity Framework seront TOUJOURS lents et inefficaces, car ils exécutent beaucoup plus de code.

Je trouve également idiot que les gens suggèrent d’optimiser les requêtes LINQ, d’examiner le code SQL généré, d’utiliser des débogueurs, de pré-comstackr, de prendre de nombreuses mesures supplémentaires, etc., c.-à-d. Perdre beaucoup de temps. Personne ne dit – Simplifiez! Tout le monde veut aller plus loin en prenant encore plus de mesures (perte de temps).

Une approche sensée consisterait à ne pas utiliser EF ou LINQ du tout. Utilisez du SQL simple. Il n’y a rien de mal à cela. Ce n’est pas parce qu’il ya une mentalité de troupeau parmi les programmeurs et qu’ils ont envie d’utiliser chaque nouveau produit, cela ne veut pas dire que c’est bon ou qu’il fonctionnera. La plupart des programmeurs pensent que s’ils incorporent chaque nouvelle pièce de code publiée par une grande entreprise, cela en fait un programmeur plus intelligent; pas vrai du tout. La programmation intelligente consiste principalement à faire plus avec moins de maux de tête, d’incertitudes et de temps. Souvenez-vous du temps! C’est l’élément le plus important, alors essayez de trouver des moyens de ne pas le gaspiller en résolvant des problèmes dans un code mal / gonflé écrit simplement pour vous conformer à des «modèles» étranges.

Détendez-vous, profitez de la vie, prenez une pause du codage et arrêtez d’utiliser des fonctionnalités supplémentaires, du code, des produits et des «modèles». La vie est courte et la durée de vie de votre code est encore plus courte. Supprimez des couches telles que LINQ, EF et autres, et votre code fonctionnera efficacement, sera mis à l’échelle et, oui, il sera toujours facile à gérer. Trop d’abstraction est un mauvais modèle.

Et c’est la solution à votre problème.

Une suggestion consiste à utiliser LINQ to Entity Framework uniquement pour les instructions CRUD à enregistrement unique.

Pour les requêtes, recherches, rapports, etc. plus complexes, écrivez une procédure stockée et ajoutez-la au modèle Entity Framework, comme décrit sur MSDN .

C’est l’approche que j’ai adoptée avec quelques sites et cela semble être un bon compromis entre productivité et performance. Entity Framework ne génère pas toujours le SQL le plus efficace pour la tâche en cours. Et plutôt que de passer du temps à comprendre pourquoi, écrire une procédure stockée pour les requêtes les plus complexes permet de gagner du temps. Une fois que vous êtes familiarisé avec le processus, append des procédures stockées à votre modèle EF ne vous pose aucun problème. Et bien sûr, l’avantage de l’append à votre modèle réside dans le fait que vous obtenez tout ce qu’il ya de bien dans l’utilisation d’un ORM.

Si vous ne récupérez que des données, cela aide grandement les performances lorsque vous demandez à EF de ne pas suivre les entités récupérées. Faites cela en utilisant MergeOption.NoTracking. EF va simplement générer la requête, l’exécuter et désérialiser les résultats en objects, mais ne tentera pas de suivre les modifications de l’entité ou autre chose de cette nature. Si une requête est simple (ne passe pas beaucoup de temps à attendre que la firebase database retourne), j’ai trouvé que le paramétrer sur NoTracking pouvait doubler les performances des requêtes.

Voir cet article MSDN sur le énumération MergeOption:

Résolution d’identité, gestion des états et suivi des modifications

Cela semble être un bon article sur la performance EF:

Performances et entity framework

Vous dites que vous avez profilé l’application. Avez-vous profilé l’ORM aussi? Il y a un profileur EF d’Ayende qui mettra en évidence où vous pouvez optimiser votre code EF. Vous pouvez le trouver ici:

http://efprof.com/

N’oubliez pas que vous pouvez utiliser une approche SQL traditionnelle avec votre ORM si vous avez besoin de gagner en performance.

S’il y a un ORM plus rapide / meilleur? Selon votre modèle object / données, vous pouvez envisager d’utiliser l’un des micro-ORM, tels que Dapper , Massive ou PetaPoco .

Le site Dapper publie des tests comparatifs qui vous donneront une idée de leur comparaison avec d’autres ORM. Cependant, il convient de noter que les micro-ORM ne prennent pas en charge le jeu de fonctions enrichies des ORM complets comme EF et NH.

Vous voudrez peut-être jeter un coup d’œil à RavenDB . Ceci est une firebase database non relationnelle (à nouveau de Ayende) qui vous permet de stocker les POCO directement sans aucun mappage nécessaire . RavenDB est optimisé pour les lectures et rend la vie des développeurs beaucoup plus facile en supprimant le besoin de manipuler le schéma et en mappant vos objects avec ce schéma. Cependant, sachez qu’il s’agit d’une approche très différente de l’utilisation d’une approche ORM et que celles-ci sont décrites sur le site du produit .

De mon expérience, le problème n’est pas avec EF, mais avec ORM approche lui-même.

En général, tous les ORM souffrent d’ un problème N + 1 , de requêtes non optimisées, etc. Mon mieux est de rechercher les requêtes qui entraînent une dégradation des performances et d’essayer d’optimiser l’outil ORM ou de réécrire ces parties avec SPROC.

J’ai trouvé la réponse de @Slauma ici très utile pour accélérer les choses. J’ai utilisé le même type de modèle pour les insertions et les mises à jour – et les performances ont explosé.

Il est logique d’optimiser après avoir profilé. Si vous découvrez que l’access à la firebase database est lent, vous pouvez convertir en utilisant des procédures stockées et conserver EF. Si vous trouvez que c’est l’EF lui-même qui est lent, vous devrez peut-être basculer vers un autre ORM ou ne pas utiliser d’ORM du tout.

Ceci est une option non-framework, non-ORM simple qui charge à 10.000 / seconde avec 30 champs environ. Fonctionnant sur un ancien ordinateur portable, probablement plus rapide que dans un environnement réel.

https://sourceforge.net/projects/dopersistence/?source=directory

J’ai également rencontré ce problème. Je déteste jeter sur EF parce que cela fonctionne si bien, mais c’est juste lent. Dans la plupart des cas, je veux juste trouver un enregistrement ou une mise à jour / insertion. Même les opérations simples comme celle-ci sont lentes. J’ai tiré 1100 enregistrements d’une table dans une liste et cette opération a pris 6 secondes avec EF. Pour moi, c’est trop long, même si cela prend trop de temps.

J’ai fini par créer mon propre ORM. J’ai tiré les mêmes 1100 enregistrements d’une firebase database et mon ORM a pris 2 secondes, beaucoup plus rapide que EF. Tout avec mon ORM est presque instantané. La seule limitation en ce moment est que cela ne fonctionne qu’avec MS SQL Server, mais il pourrait être modifié pour fonctionner avec d’autres utilisateurs comme Oracle. J’utilise MS SQL Server pour tout maintenant.

Si vous souhaitez essayer mon ORM, voici le lien et le site Web:

https://github.com/jdemeuse1204/OR-M-Data-Entities

Ou si vous souhaitez utiliser nugget:

PM> Install-Package OR-M_DataEntities

La documentation est là aussi

Entity Framework ne doit pas provoquer de goulots d’étranglement majeurs. Les chances sont qu’il existe d’autres causes. Vous pouvez essayer de passer d’EF à Linq2SQL, les deux ont des fonctionnalités de comparaison et le code devrait être facile à convertir, mais dans de nombreux cas, Linq2SQL est plus rapide qu’EF.

Nous avons une application similaire (Wcf -> EF -> firebase database) qui exécute facilement 120 requêtes par seconde, alors je suis plus que sûr que EF n’est pas votre problème, cela dit, j’ai constaté des améliorations de performances majeures avec les requêtes compilées.

J’ai utilisé EF, LINQ to SQL et dapper. Dapper est le plus rapide. Exemple: j’avais besoin de 1000 enregistrements principaux avec 4 sous-enregistrements chacun. J’ai utilisé LINQ pour SQL, cela a pris environ 6 secondes. J’ai ensuite basculé sur Dapper, récupéré 2 jeux d’enregistrements à partir de la procédure stockée unique et pour chaque enregistrement ajouté les sous-enregistrements. Temps total 1 seconde.

De plus, la procédure stockée utilisée pour les fonctions de tableau avec cross apply, j’ai trouvé les fonctions de valeur scalaire très lentes.

Mon conseil serait d’utiliser EF ou LINQ to SQL et pour certaines situations, passer à dapper.