Cet article a pour objectif de décrire notre approche à propos de l'accès aux données.
En préambule, un petit aparté sur Entity framework, qui s'inscrit dans la continuité de l'histoire douloureuse des ORM de Microsoft.
Microsoft a sorti en 2008 la V1 d'Entity Framework. Elle a suscité de nombreuses réactions, plutôt négative de la communauté. Trop intrusif, trop de code à écrire, trop compliqué à utiliser, trop d'inconvénients pour peu de bénéfices. Microsoft a écouté, et a décidé de construire la V2, en transparence, et en communiquant au fur et à mesure de son évolution. Ca se passe ici, sur le blog de l'équipe d'EF, et la Wish list (77 pages de Forums !) s'est arrêtée au 15 janvier.
Malgré cela, l'approbation de la communauté n'est pas encore gagné.
Un article évoque la gestion du ChangeTracking, fonctionnalité inhérente à tout ORM qui se respecte. Ils ont plus ou moins décidé qu'ils allaient fournir des API pour que les développeurs implémentent eux-mêmes leur gestion du ChangeTracking: que de choses à connaître, et que de code à écrire.
Autre exemple, la gestion des Foreign Keys. Apparemment, certains voudraient voir les FK dans leurs entités, et d'autres non, car ils considèrent que l'entité est polluée par une FK. D'où une gestion de FK hybride, les Independant Associations apportées par .Net 4.0; encore quelque chose de compliqué et d'artificiel qui nécessitera apprentissage.
Alors que l'usage du DataSet simplifierait les problèmes, ils évitent soigneusement de l'utiliser, avec des arguments discutables.
Sami Jabber a parfaitement raison quand il écrit à propos d'Entity FrameWork: "Un modèle conceptuel de données qui permet d'abstraire du XML, du relationnel, de l'objet et des DataServices (pour Astoria) a un nom, cela s'appelle de la magie noire".
Notre vision est nettement plus simple, pragmatique, compréhensible tant pour sa lecture, que pour son utilisation, souple, dynamique, peu intrusif, et requiert l'écriture d'un minimum de code.
Des données relationnelles sont persistées quelque part (un fichier, une base de données ? peu importe !), et il serait pratique d'y avoir accès "facilement". Inversement, des données relationnelles sont en mémoire, et on souhaite les persister quelque part (un fichier, une base de données ? peu importe !), sans trop d'efforts. J'insiste sur le caractère relationnel des données, car c'est le coeur du problème, ou plutot le coeur de la solution.
Plus précisément, on souhaite écrire le moins de code possible (évitons le SQL) et ne pas être intrusif vis-à-vis des Entités que l'on manipule.
Et s'il n'y a pas de solution magique, je maintiens qu'il y a plus à gagner à utiliser le DataSet qu'à ne pas le faire. Et pas seulement pour le ChangeTracking, mais pour beaucoup d'autres; serialisation, dynamisme, filtre, tri, Merge, gestion événementielle.
Et s'il y a quelques défauts, essayons de les gommer, plutôt que de jeter le bébé avec l'eau du bain.
-
Intellisense: toujours difficile à concilier avec le dynamisme, nous apportons une solution. C'est du typage fort pour le code, côté serveur, et du typage dynamique pour le transport et le Binding, le mariage idéal des 2 mondes.
-
Validation: les ExtendedProperties permettent de stocker des attributs complémentaires et manquants. Des validateurs standards (Min, Max, Regex), mais aussi Custom avec des Commandes de Validation.
-
Navigation: quelques fonctions génériques de navigation, en fonction du typage fort, mais aussi du typage souple, permettent de naviguer de façon extrêmement conviviale et évite la manipulation verbeuse du DataSet qui fait souvent peur au développeur.
Aspectize Entity Designer est l'Outil de Design du modèle conceptuel des données, c'est à dire les Entités et les Relations. De ForeignKey, il n'est point question; nous insistons sur ce point, la Relation existe à part entière, c'est fondamental pour l'approche. Sa cardinalité est définie, mais elle n'a pas d'implication sur le modèle. Cela permet de garantir l'indépendance des Entités. Peu importe si ma Category est liée à un Product ou à autre Chose, cela n'influe pas la nature de mon Entité Category.

Nous pouvons ajouter de nombreux attributs :
- MustPersist: permet de dire si certaines Entités ou Champs ne sont pas sauvegardés. Pratique, quand certaines entités ne servent qu'au calcul ou a l'IHM. A noter que les Relations peuvent également être non persistence.
- Validators: permet de définir un Validateur, écrit en .Net, qui sera toujours appelé lors d'un CommandBinding configuré sur cette commande.
- Triggers: permettent de définir des commandes qui seront automatiquement appelées lorsque l'on impactera une Entité, en fonction du State (Add, Delete, Update).
Un des attributs les plus intéressant est sans doute le Temporel, qui permet de définir automatiquement la dépendance au temps d'une donnée: dans mon exemple, le prix du produit est historisé, et est donc stocké dans une autre table. L'intérêt de l'attribut Temporel est que cela est complètement transparent d'un point de vue logique: j'ajoute même un autre champ qui me permet d'avoir le prix courant, sans être obligé d'interroger systématiquement l'historique des prix.

EntityManager est notre composant d'accès aux données. Il permet, via une API extrêmement simple de faire tout ce qu'on veut pour récupérer des données et les sauvegarder.
IDataManager
dm = EntityManager.FromDataBaseService("MyDataService");
Pour récupérer une Category à partir de son Id:
Category
category = dm.GetEntity<Category>(id); ou tous les Product associés à une Category, selon la relation CategoryProduct (on pourrait imaginer avoir plusieurs relations entre les mêmes entités):
dm.GetAssociated<
Product, CategoryProduct>(id);
Accessoirement, c'est la même API pour naviguer dans les données en mémoire, à partir du DataSet (ce qui évite la manipulation verbeuse):
List
<Product> products = category.GetAssociatedInstances<CategoryProduct>();
Et pour la sauvegarde, c'est encore plus simple. La seule méthode Save, sauvegarde toutes les lignes du DataSet en fonction des changements:
L'énorme avantage est de tirer parti à la fois du typage fort proposé par Entity Designer, et du typage dynamique géré par le DataSet.
Ainsi, le chargement d'une Category peut s'écrire également:
dm.LoadData("ADWData.Category[Id = id]");
ou avec les Product associés:
dm.LoadData(
"ADWData.Category[Id = id].CategoryProduct.Product");
mais aussi ajouter un nouveau Product à une Category:
uiService.AddRow("ADWData.Category.CategoryProduct.Product");
Il est du coup, extrêmement facile d'écrire des services d'accès aux données; nous verrons plus tard que nous n'avons même pas à écrire ces services d'accès aux données, car ils peuvent se déduire du DataBinding. L'Application charge toute seule les données qu'elle affiche, en fonction des DataBinding configurés sur les contrôles.
Difficile de faire plus simple; aucune intrusivité, tous les scénarios d'accès aux données Lazy ou non sont couverts, de façon extrêmement limpide et le DataBinding se charge du reste !