novembre
2008
Une des choses qui a accompagné la sortie d’entity framework, c’est les (nombreuses) annonces de support de la technologie de mapping O/R de Microsoft par divers fournisseurs de base de données ou d’éditeurs externes.
Le néophyte (comme moi pour l’instant ;)) c’est donc dit : « Chouette, je vais pouvoir générer un model commun à divers SGBD (en faisant attention au problème de type de données évidement) et choisir avec quelle base de données je vais travailler dans mon fichier de configuration. »
Malheureusement tout ceux qui ont essayé on vite déchanté (moi le premier). En effet le model et les divers composants générés par les designers fournissent tous un code dépend du provider avec lequel ils travaillent. Ainsi si vous générer le .edmx avec une base de données SqlServer la totalité des informations générées est dépendante du provider SqlServer.
Évidement la solution de créer un edmx par SGBD à supporter ne fonctionne pas. En effet vous vous retrouvez avec les classes générés en double. Et modifier le code généré n’est pas une bonne idée car à la moindre régénération il faudra recommencer les modifications.
Toutefois il existe une solution. Préparez vous à plonger dans les entrailles d’entity framework, c’est « sioux ».
Première chose à constater : Dans la chaine de connexion généré par le designer entity framework il y a le nom du provider à utiliser.
Dans cet exemple j’utilise le provider de devart qui fourni un support entity framework pour MySql. C’est ce qui est identifié par
provider=CoreLab.MySql
Le premier réflexe est donc de récupérer une chaine de connexion pour SqlServer et de l’ajouter dans le fichier de configuration. Ensuite on test le tout en utilisant le constructeur avec argument de l’ObjectContext :
On compile le tout, pas de soucis, et on lance. Dès la première tentative d’accès aux données une exception est levé et nous indique que le provider décrit dans la chaine de connexion est différent de celui décrit dans le fichier de mapping. Ah bon ? il semble donc que le provider qui a servit à générer le mapping soit inscrit quelque part et que l’information soit utilisé au runtime.
Un petit tour dans le fichier edmx avec notepad++ va nous en apprendre beaucoup. En effet pour ceux qui n’ont jamais été curieux, EntityFramework se base sur un ensemble de 3 « groupes » xml pour décrire le mapping. Ces 3 groupes sont décris dans le fichier edmx et sont inclus dans la balise
Il s’agit du ssdl qui contient les informations sur le stockage des données, le csdl qui contient les informations sur le mapping table/classe et le C-S qui contient les informations sur le mapping propriété/colonne.
Revenons à notre problème du moment : Le provider est inscrit dans le ssdl et plus précisément dans la balise
Le premier réflexe est de copier la totalité des informations contenues entre les balises
En plus le nœud parent de la balise
Tout content on va donc recompiler notre application. Pas de problème à la compilation ! Super, armé de notre fichier de configuration qui possède deux chaines de connexion décrivant deux providers, on espère que le système est intelligent, qu’il va choisir le bon ssdl en fonction du provider et on se lance. Oui mais non, même problème.
En effet bien le modèle xml accepte deux balises Schema, il semblerait qu’il n’y en ai qu’une d’utilisé. Ce n’est donc pas la bonne piste.
Retour à notre fichier de configuration. Après une observation plus attentive nous remarquons que la chaine de connexion commence par décrire où se trouve les metadatas de description du mapping. Le « res:// » ressemble quand même beaucoup à une sorte de protocole pour accéder au ressources de l’application …
Ni une ni deux, un petit coup de reflector sur l’exe et hop nous remarquons dans les ressources la présence de 3 fichiers correspondant à ce qui est décrit dans notre fichier de configuration : DAL.POC.ssdl, DAL.POC.msl et DAL.POC.csdl.
L’idée suivante est d’aller chercher dans un autre exécutable ces 3 fichiers générés pour un autre SGBD (dans notre exemple pour SqlServer).
Toujours avec notre ami reflector, on extrait les fichiers (cliques droit -> enregistrer sous). Retour dans notre projet principal. Nous allons ajouter les 3 fichiers extraient à notre projet et les inclure en tant que ressources incorporés. (Propriété -> Action de génération). Il ne reste plus qu’à modifier la chaine de connexion pour SqlServer afin d’indiquer ou se trouve les ressources pour SqlServer (la encore Reflector peut vous aider):
Une fois cela fait, vous pouvez utiliser votre model de données avec n’importe quelle base du moment que vous embarquez les fichiers de description du mapping.
Notons tout de même que les types de données utilisé dans les bases doivent être identique et ,à plus forte raison, doivent être mappés de la même façon !
2 Commentaires + Ajouter un commentaire
Archives
- juillet 2012
- mars 2012
- février 2012
- novembre 2011
- octobre 2011
- mars 2011
- novembre 2010
- octobre 2010
- septembre 2010
- août 2010
- avril 2010
- février 2010
- janvier 2010
- novembre 2009
- octobre 2009
- septembre 2009
- juin 2009
- mai 2009
- avril 2009
- mars 2009
- février 2009
- janvier 2009
- décembre 2008
- novembre 2008
- octobre 2008
- septembre 2008
- août 2008
- juin 2008
- mai 2008
- avril 2008
- février 2008
- mai 2007
- avril 2007
- mars 2007
- février 2007
- janvier 2007
Je suis entièrement d’accord avec toi concernant le fait que le mapping n’est pas de la magie noire. Toutefois la ou Entity Framework pêche ce n’est pas sur l’incompatibilité des types de données des différents SGBD mais bien sur la souplesse de l’outil. Je suis pas d’accord avec toi sur le fait « qu’il faut bien une liaison avec le provider spécifique de la base de données ». En effet le provider de la base de données est clairement un élément qui n’intervient qu’au moment de l’exécution. La seule chose qu’a besoin de savoir EntityFramework au moment de la conception c’est comment les données sont stockées (Table/vue/FK, etc, etc) et leurs types. C’est la responsabilité du développeur que de s’assurer que les divers SGBD visés peuvent utiliser les même types de données. Comme tu le dit si justement cela restreint le nombre de type qui peuvent être utilisé, mais on a rien sans rien. De plus les providers sont la pour faire « tampon » entre le code managé et les types de données supportés dans les SGBD.
De plus comme tu le dit si justement, à la base EntityFramework utilise non pas un fichier mais bien 3. c’est 3 fichiers ont tous un rôle et ce que je reproche à VS c’est de tout regrouper en un seul, et surtout de tout inclure directement dans les ressources de l’application. Si ces fichiers étaient externe il serait bcp plus simple de définir des models pour plusieurs SGBD.
Au final tous les éléments pour faire en sorte que plusieurs SGBD soient supporté par le même model de code sont en place mais à cause de la façon dont ils sont gérés par VS cela devient extrement compliqué. Si l’outil de génération de VS respecté la séparation des couches comme le respect EdmGen.exe il n’y aurait plus aucun problèmes. Et c’est bien cela que je trouve dommage
Il est évident que la solution, vue comme ça, n’est ni limpide ni directe… Toutefois il faut se rappeler deux ou trois choses : d’une part le mapping O/R ce n’est pas de la magie noire, il faut bien une liaison avec le provider spécifique de la base de données. Le problème n’est donc pas tant celui de Entity Framework que celui des concepteurs de bases de données qui n’ont jamais su s’entendre sur un standard, ni même sur un SQL vraiment standardisé.
D’autre part, il faut en effet avoir conscience que l’EDM se base sur un mapping décrit dans un fichier XML et qu’une partie décrit spécifiquement le « data store » (le stockage de données). Le modèle lui-même est donc bien découplé de la base physique et c’est justement le rôle du ssdl que de permettre l’adaptation à une base cible donnée.
Enfin, et c’est peut être ce qui explique le chemin qui semble tortueux pour arriver au résultat, si Linq To Entities est conçu pour fonctionner avec de nombreuses bases de données il ne faut pas entendre « simultanément ». Même si cela reste possible. Linq To Entities permet de se lier à une base Oracle ou MySQL ce que Linq To SQL ne sait pas faire (limité à SQL Server) mais cela s’arrête là. Changer de cavalier en cours de runtime (ou au moment du lancement d’une appli ce qui revient au même) en fonction d’un paramétre de configuration ce n’est pas forcément le but premier de LINQ to Entities.
On touche là le vieux fantasme des développeurs : que leurs appications s’adaptent automatiquement à n’importe quelle base de données selon le choix de l’utilisateur final…
Même si la réalisation de ce fantasme semble plus proche, voire réalisable avec un peu d’astuces grâce à l’Entity Framework, c’est oublié que les bases de données, comme il est dit dans cet article, doivent avoir exactement la même structure et que tous les types des champs soient parfaitement et identiquement compatibles… Si on sait se limiter à un noyau presque standard de types SQL, cela peut passer, mais si on désire avoir des implémentations vraiment performantes utilisant certains types de données spécifiques à une base pour en exploiter les avantages, on retombe dans le fantasme de la tour de babel.
Gardons bien à l’esprit que si l’Entity Framework et Linq to Entities représentent une avancée fantastique sur toutes les solutions qui existaient jusqu’alors, certains problème de fond liés aux bases elles-mêmes ne peuvent disparaître totalement. La souplesse de l’Entity Framework permet de trouver des solutions, mais, par force, cela complique un peu le travail du développeur…