Entity Framework avec de grands systèmes - comment diviser les modèles?

50

Je travaille avec une base de données SQL Server avec plus de 1000 tables, quelques centaines de vues et plusieurs milliers de procédures stockées. Nous envisageons de commencer à utiliser Entity Framework pour nos nouveaux projets et nous travaillons à notre stratégie pour le faire. Ce que je raccroche, c’est la meilleure façon de scinder les tables en différents modèles (EDMX ou DbContext si l’on passe au code en premier). Je peux penser dès le départ à quelques stratégies:

  • Divisé par schéma
    Nous avons nos tables divisées en probablement une douzaine de schémas. Nous pourrions faire un modèle par schéma. Ce n'est pas parfait, cependant, car dbo finit toujours par être très volumineux, avec plus de 500 tables / vues. Un autre problème est que certaines unités de travail finiront par effectuer des transactions couvrant plusieurs modèles, ce qui ajoute à la complexité, bien que je suppose que EF rend cela assez simple.
  • Diviser par intention
    Au lieu de se préoccuper des schémas, divisez les modèles par intention. Nous aurons donc différents modèles pour chaque application, projet, module ou écran, en fonction de la granularité souhaitée. Le problème que je vois avec ceci est que certaines tables doivent inévitablement être utilisées dans tous les cas, telles que User ou AuditHistory. Est-ce que nous les ajoutons à chaque modèle (viole DRY je pense), ou sont-ils dans un modèle séparé utilisé par chaque projet?
  • Ne divisez pas du tout - un modèle géant
    Ceci est évidemment simple du point de vue du développement, mais d'après mes recherches et mon intuition, il semble que les performances puissent être terribles, à la fois au moment de la conception, de la compilation et éventuellement de l'exécution.

Quelle est la meilleure pratique pour utiliser EF avec une base de données aussi volumineuse? Quelles stratégies utilise-t-on spécifiquement dans la conception de modèles pour ce volume d’objets de base de données? Y a-t-il des options pour lesquelles je ne pense pas que cela fonctionne mieux que ce que j'ai ci-dessus?

En outre, est-ce un problème dans d'autres ORM tels que NHibernate? Si oui, ont-ils trouvé de meilleures solutions que EF?

RationalGeek
la source
"le fait de devoir effectuer des transactions couvrant plusieurs modèles, ce qui ajoute à la complexité" Veuillez noter que vous devez activer le coordinateur de transactions distribuées Microsoft. Une fois que vous avez cela en place, il devrait être simple d'accomplir ce dont vous parlez.
Tjaart
@ Tjaart merci. J'ai déjà utilisé MS DTC auparavant et bien que ce soit assez simple, cela ajoute à la complexité au-delà d'un simple DB txn, donc je veux l'éviter autant que possible.
RationalGeek
2
4 ans plus tard, qu'avez-vous décidé et que recommanderiez-vous maintenant?
Rory

Réponses:

31

Personnellement, j'ai essayé de créer un schéma énorme pour toutes mes entités sur un projet assez complexe mais de petite taille (~ 300 tables). Nous avions une base de données extrêmement normalisée (normalisation de 5ème forme (je le dis vaguement)) avec beaucoup de relations "plusieurs à plusieurs" et une application extrême de l'intégrité référentielle.

Nous avons également utilisé une stratégie "d'instance unique par demande", ce que je ne suis pas convaincu non plus d'avoir aidé.

Lors de la création de listes "définies explicitement" simples et raisonnablement plates, la recherche et l’enregistrement des performances étaient généralement acceptables. Mais lorsque nous avons commencé à creuser des relations profondes, la performance semblait prendre des creux drastiques. Comparé à un proc stocké dans ce cas, il n'y avait aucune comparaison (bien sûr). Je suis sûr que nous aurions pu peaufiner la base de code ici et là pour améliorer les performances. Cependant, dans ce cas, nous avions simplement besoin d'optimiser les performances sans analyse en raison de contraintes de temps, et nous sommes revenus au processus stocké (toujours cartographié). via EF, parce que EF fournissait des résultats fortement typés), nous n’avions besoin de cela que pour nous replier dans quelques zones. Lorsque nous avons dû parcourir la base de données pour créer une collection (en utilisant .include () sans ménagement), la performance était sensiblement dégradante, mais nous en demandions peut-être trop.

Donc, sur la base de mon expérience, je recommanderais de créer un fichier .edmx distinct par intention. Générez uniquement ce que vous utiliserez en fonction de l'ampleur de ce besoin. Vous pouvez avoir des fichiers .edmx de taille réduite pour des tâches spécifiques, puis des fichiers volumineux dans lesquels vous devez traverser des relations complexes pour créer des objets. Je ne sais pas où se trouve cet endroit magique, mais je suis sûr qu'il y en a un ... lol ...

Honnêtement cependant, mis à part quelques pièges que nous avons en quelque sorte vus à venir (complexe traversant), l’immense .edmx a bien fonctionné du point de vue du "travail". Mais vous devrez faire attention à la magie de "réparation" que le contexte fait derrière la scène si vous ne le désactivez pas explicitement. En plus de synchroniser le .edmx lorsque des modifications sont apportées à la base de données, il était parfois plus facile d’effacer toute la surface et de recréer les entités, ce qui prenait environ 3 minutes, donc ce n’était pas si grave.

C'était tout avec EntityFramework 4.1. J'aimerais aussi connaître votre choix final et votre expérience.

Et en ce qui concerne votre question sur nHibernate, c’est une question de canette de ver à mon avis, vous allez aboyer des deux côtés de la barrière ... j’entends beaucoup de gens dénigrer EF pour pouvoir dénigrer sans travailler à travers le défis et compréhension des nuances propres à EF lui-même .. et bien que je n’aie jamais utilisé nHibernate en production, en général, si vous devez créer manuellement et explicitement des éléments tels que des mappages, vous obtiendrez un contrôle plus fin. peut glisser-déposer, générer et commencer à créer des requêtes et des requêtes en utilisant LINQ, je pourrais vous donner une merde sur la granularité.

J'espère que ça aide.

hanzolo
la source
1
FYI - Il existe un utilitaire de cartographie NHibernate qui rend ces mappages TRÈS simples et automatisés.
jars
@ganders - At-il une interface utilisateur et comment est-ce l'intégration IDE? Je suppose que vous le dirigez vers une source de données et qu'il respecte l'intégrité référentielle, le parcours des objets et crée les objets de mappage?
hanzolo
1
Oui c'est le cas (interface graphique). Je n'ai eu aucun problème avec cela jusqu'à présent. Utilisé sur 4 ou 5 projets / sites différents. Remarque: Je l’utilise avec NHibernate Fluent, qui effectue le mappage dans du code c #, pas dans des fichiers config / xml. Voici un lien: nmg.codeplex.com
jars
13

Permettez-moi de commencer par une simple clarification: je n’ai pas d’expérience avec une base de données aussi volumineuse, le reste de ma réponse n’est donc pas basé sur l’exemple du monde réel.

Vous avez donc une base de données BIG et vous souhaitez l’utiliser avec ORM / EF. J'irais avec le deuxième choix. Voici ma simple explication pourquoi:

  • La cartographie ajoute de la complexité. Il n’est pas nécessaire d’ajouter de la complexité aux entités dont votre application / projet / module actuel n’a jamais besoin, mais ne donnez pas un niveau de granularité trop bas. Avoir un jeu de mappage séparé par écran ne vous aidera pas aussi bien.
  • Vous voulez atteindre l'unité de travail. Vous devriez pouvoir spécifier les modules dont le module a besoin dans la plupart des cas (pas nécessairement dans tous les cas). Si vous mettez ces tables dans un ensemble de mappage unique, vous serez en mesure de gérer la lecture et la modification des données par une seule instance de contexte - c'est ce qui devrait être votre cible ultime.
  • Je ne sais pas ce que vous entendez exactement par modèle, mais même avec différents ensembles de mappage, vous pouvez partager des classes entre des ensembles de mappage utilisant les mêmes types d'entité. Donc, si vous utilisez la table User dans deux modules, vous n'avez pas besoin que deux classes User représentent la même chose. Vous pouvez toujours utiliser une seule table et en cas de mappage de code (alias code-first), vous pouvez même définir le mappage une fois et le charger dans plusieurs jeux de mappage afin que le principe DRY ne soit pas enfreint, mais que l'approche du code d'abord présente davantage de limites. aux vues et procédures stockées. EDMX rend cela plus difficile. Vous pouvez toujours réutiliser des classes mais la réutilisation du mappage est impossible.
  • Qu'en est-il des requêtes entre modules? Ces questions peuvent arriver, mais pour être honnête, tout ne doit pas être traité par EF. Vous pouvez tirer parti de EF pour les cas courants afin de simplifier l’accès normal aux données, mais si vous avez quelque part besoin d’une requête spéciale qui joint des tables appartenant à 5 modules différents, vous pouvez simplement l’exécuter directement ou l’envelopper dans une procédure stockée. Le remplacement à 100% de l'accès aux données natives peut être difficile, complexe et contre-productif.
  • Le dernier point est simplement pratique: je ne pense pas que l’outillage de VS soit prêt à fonctionner avec un aussi grand ensemble d’objets - ni dans le concepteur, ni même avec l’outil d’importation. J'avais l'habitude de travailler sur une base de données très volumineuse avec un accès aux données traditionnel et un projet de base de données SQL dans VS2008 - l'expérience utilisateur avec un projet complexe était très mauvaise. Vous devez limiter le nombre de tables utilisées - la limite maximale pour le concepteur devrait se situer entre 100 et 200, mais même 100 tables gérées par un seul contexte (ensemble de mappages) sonne comme une trop grande responsabilité pour une classe (supposons que vous ayez 100 propriétés définies). exposé sur le contexte - cela ne ressemble pas à un bon design).
Ladislav Mrnka
la source
4

Je dirais que vous ne pouvez pas décider de ce genre de question d'un point de vue technique. Je vous recommanderais de construire votre architecture en fonction de vos cas d'utilisation (user stories, etc.). Commencez par trouver vos objets métier. Un objet entité n'est pas par défaut un objet métier. En règle générale, vous aurez un objet métier devant les objets entité. Vous pouvez ensuite décider progressivement de ce dont vous avez réellement besoin, en fonction des besoins de l'utilisateur.

"Un bon architecte maximise le nombre de décisions non prises." Robert C. Martin

http://cleancoder.posterous.com/architecture-deference

ollins
la source
3

J'utilise une approche hybride - les éléments OLTP sont gérés par EF alors que les opérations lourdes telles que les insertions par lots, les mises à jour en masse, les requêtes de rapport, etc., sont gérées par les processus stockés. Cela facilite également le chemin de migration si vous ne faites pas une réécriture complète de votre couche de données en une fois.

Nik
la source
Cela semble être une bonne stratégie, mais n'aborde pas vraiment la question de la division des entités entre différents modèles EF. Avez-vous toutes les entités dans un modèle ou divisez-vous et conquiers d'une manière ou d'une autre?
RationalGeek
1
Si les performances OLTP sont suffisantes avec l'approche du modèle complet, optez pour cela. Vous pouvez toujours le casser plus tard si nécessaire, mais le moyen le plus rapide et le plus agile est de tout charger. Vous pouvez ne jamais avoir besoin des gains de performances que vous obtenez en les dissociant. Vous perdriez donc du temps et en rendant votre système plus compliqué sans raison. Vient ensuite la question de savoir quel modèle colleriez-vous à une nouvelle table / entité lorsque vous déciderez d’agrandir. Et que se passe-t-il lorsque vous devez exécuter une mise à jour sur plusieurs modèles? Epargnez-vous le mal de tête à moins que vous n'ayez vraiment pas d'alternative.
Nik
J'ai oublié de mentionner que vous pouvez toujours modifier vos performances lorsque vous accédez à vos données. Examinez les options de chargement paresseux / enthousiaste et les entités enfant que vous importez. Je ne vois aucune raison pour laquelle un modèle complet se comporterait moins bien qu'un modèle plus petit si vous ne chargiez pas d'arborescences d'objets massives.
Nik
Je dirais que les arbres à objets massifs et une structure de données normalisée vont de pair avec les grands schémas
hanzolo
Vous contrôlez le degré ou le degré de saturation du graphe d'objets.
Nik