Data Tier Modeler : Un concurrent d'ObjectSpaces est né !

Le DTM est un logiciel permettant à un développeur de générer l'ensemble du code source contenant toute la logique d'accès à une base de données à partir d'objets (Entités, collections, ...). L'ensemble du Framework repose sur ADO.NET pour effectuer les opérations de chargement, modification ou suppression de données.
Cet outil se rapproche du Framework ObjectSpaces avec quelques différences notables :
Le code source est pré-généré, et non pas généré à la volée via la reflexivité dans le but d'optimiser les performances.
Le code source est optimisé suivant la base de données (SGBD) utilisée pendant la phase de génération de code. La couche logicielle peut donc faire appel à des fournisseurs d'accès optimisés, avec ou sans procédures stockées.
La modélisation du code à générer est réalisée à partir d'un diagramme de classes UML (mais aussi depuis une base existante ou un fichier XML). La base de données correspondante est via l'outil.
L'outil prend en compte l'héritage d'objets métier (entités), les associations complexes (many-to-many), des classes d'association, des interfaces avec la génération d'une classe totalement intégrée au Framework DTM via la génération dynamique de code.
DTM s'appuie pour fonctionner sur un noyau écrit en C#. L'ensemble du code source est généré à partir d'interfaces graphiques prenant en compte des diagrammes de classes au format XMI (XML Metadata). L'avantage de cette technique est de laisser la possibilité à l'utilisateur de concevoir ces diagrammes avec n'importe quel outil de modélisation du marché (Rose, TogetherJ, ...). La génération de code s'effectue ensuite simplement à partir de l'outil DTM. Le modèle de développement est donc totalement transparent pour le développeur qui se focalise uniquement sur son modèle UML.
DTM, tout comme ObjecSpaces, se base sur une méthode "par extension de code" (enhancer) permettant de générer un objet technique dérivant des classes techniques du Framework. Dans le cas de DTM, tout objet généré est du type PObject et hérite d'un certain nombre de méthodes destinées à assurer le cycle de vie de l'objet. Vous trouverez les méthodes :
Load() : charge un objet en mémoire ainsi que l'ensemble de ces attributs (excepté les collections)
Delete() : supprime l'objet du cache et de la structure de stockage
Synchronize() : synchronise le cache mémoire et la base
Les relations entre les classes sont aussi implémentées, il suffit pour cela d'utiliser les accesseurs (get(), set()) qui procéderont au chargement des données associées à l'objet en mémoire. Cette méthode permet de réaliser un lazy loading dans le cas du chargement de volumes de données important.
L'accès aux données est paramétrable en Lecture ou Ecriture afin de tirer parti des fonctionnalités du cache objets. Tous les accès en base sont transactionnels avec deux mode de gestion :
Transactions automatiques (géré par le conteneur)
Transactions manuelles (géré par l'utilisateur à l'aide de l'API ADO.NET)
La figure suivante illustre l'architecture du produit Data Tier Modeler.

Le langage de requête utilisé est OPath. Chaque objet dispose donc d'opérations de tris mais aussi de binding (association des données) pour les composants visuels .NET.
PersonCollection pc = PersonCollection.GetObject("Person[Firstname not
like 'First%']");
Console.WriteLine("Non triés ----");
Pour plus d'information du OPath, reportez vous à l'article sur ObjectSpaces (DotNetGuru).
Prenons un exemple concret afin de tester les fonctionnalités du DTM. Le diagramme de classes suivant est destiné à être stocké en base.

Une fois générateur invoqué, voici le code source vous permettant d'utiliser votre base de données de manière objet en respectant totalement le paradigme des architectures n-tiers :
using System;
using Meta;
namespace Test
{
class TestDTM
{
[STAThread]
static void Main(string[] args)
{
DataBase.SetDefaultConnection("Server=localhost;Database=DTM;UID=sa;PWD=");
DataBase.DefaultAccessMode = AccessMode.Write;
//Populate();
// Création d'objets : tester plusieurs fois d'affilée pour voir la rapidité
PersonCollection pc = new PersonCollection();
pc.LoadAll();
foreach(Person p in pc)
{
Console.WriteLine("{0} {1} ", p.Firstname, p.Lastname);
}
for(int i=1; i<=2000; i++)
{
Person p;
pc.Add(p = new Person("Last", "First " + i.ToString()));
}
if(pc.Synchronize())
Console.WriteLine("Enregistrement réussi : {0} personnes", pc.Count);
}
static void Populate()
{
DataBase.DefaultAccessMode = AccessMode.Write;
Course c1 = new Course("Maths", 4);
Course c2 = new Course("Mecanique", 11);
Course c3 = new Course("Modélisation", 9);
Course c4 = new Course("Automatique", 15);
Course c5 = new Course("Informatique", 10);
Course c6 = new Course("UML", 5);
Course c7 = new Course("Java", 6);
Course c8 = new Course("Architecture", 15);
Course c9 = new Course("CE", 30);
Course c10 = new Course("Chant", 20);
Course c11 = new Course("Musique", 21);
c11.Synchronize();
CourseCollection cc = new CourseCollection();
cc.Add(c1);
cc.Add(c2);
cc.Add(c3);
cc.Add(c4);
cc.Add(c5);
cc.Add(c6);
cc.Add(c7);
cc.Add(c8);
cc.Add(c9);
cc.Add(c10);
cc.Synchronize();
Teacher t1 = new Teacher("Meyer", "Thomas", "");
t1.HisCourses = cc;
t1.RemoveToHisCourses(c5);
t1.HisCourses.Remove(c1);
t1.Synchronize();
Teacher t2 = new Teacher("Dupont", "Regis", "");
t2.HisCourses.Add(c1);
t2.HisCourses.Add(c4);
t2.HisCourses.Add(c5);
t2.Synchronize();
Teacher t3 = new Teacher("tutu", "titi", "");
t3.HisCourses.Add(c4);
t3.HisCourses.Add(c2);
t3.HisCourses.Add(c3);
t3.Synchronize();
Teacher t4 = new Teacher("Aurélien", "Grégoire", "");
t4.AddToHisCourses(c10);
t4.AddToHisCourses(c8);
t4.AddToHisCourses(c5);
t4.AddToHisCourses(c1);
t4.AddToHisCourses(c9);
t4.Synchronize();
Teacher t5 = new Teacher("Schnitzi", "Roland", "");
t4.AddToHisCourses(c7);
t4.AddToHisCourses(c6);
t4.AddToHisCourses(c5);
t4.AddToHisCourses(c4);
t5.Synchronize();
Address a1 = new Address(35, "Frères Lumiere", 68093, "Mulhouse");
Student s1 = new Student("toto", "", 0);
s1.HisCourses.Add(c2);
s1.HisCourses.Add(c4);
s1.AddToHisCourses(c11);
s1.HisAddress = a1;
s1.Synchronize();
Address a2 = new Address(3,"Forêt",68500,"Jungholtz");
Student s2 = new Student("Tutu", "", 0);
s2.HisCourses.Add(c1);
s2.HisCourses.Add(c3);
s2.HisCourses.Add(c5);
s2.HisAddress = a2;
s2.Synchronize();
Address a3 = new Address(4,"Cigognes",68360,"Soultz");
Student s3 = new Student("Titeuf", "", 0);
s3.HisCourses.Add(c10);
s3.HisAddress = a3;
s3.Synchronize();
Address a4 = new Address(11,"Creuse",68500,"Jungholtz");
Student s4 = new Student("Serveaux", "Sébastien", 5);
s4.AddToHisCourses(c4);
s4.AddToHisCourses(c5);
s4.AddToHisCourses(c6);
s4.AddToHisCourses(c7);
s4.AddToHisCourses(c8);
s4.HisAddress = a4;
s4.Synchronize();
Account acc1 = new Account("log1", "pass1", "info");
bool isOk4 = acc1.Synchronize();
Account acc2 = new Account("Titeuf", "tof", "games");
s3.AddToHisAccounts(acc1);
s3.HisAccounts.Add(acc2);
s3.AccessRights[acc2].Rights = Rights.read;
s3.AccessRights[acc1].Rights = Rights.root;
bool isok1 = s3.Synchronize();
Account acc5 = new Account("Seb", "s", "evaluant");
acc5.ItsStudent = s4;
acc5.AccessRights.Rights = Rights.write;
s4.AccessRights[acc5].TimeTables = "9h00-12h00";
bool isok2 = s4.Synchronize();
Account acc6 = new Account("Tutu", "pfff", "english");
new AccessRights(s2, acc6, Rights.root, "12h00-14h00");
bool isok3 = s2.Synchronize();
}
}
}
Aujourd'hui le tarif de DTM n'a pas totalement été arrêté par la société Evaluant qui développe le produit. Espérons que la politique tarifaire sera à la hauteur de nos espérances pour promouvoir cet outil à grande échelle.
Si pour l'heure, DTM n'est pas un Framework totalement finalisé avec très peu de documentation disponible, il possède d'ores et déjà d'énormes qualités. Son architecture bâtie autour du format XMI lui permet d'assurer une portabilité des modèles, argument commercial indéniable. Si aujourd'hui ObjectSpaces demeure fortement lié à Visual Studio seul capable aujourd'hui de générer le fameux fichier XML de mapping Objet/Relationnel, DTM, lui, joue la carte de l'ouverture.
Data Tier Modeler est un excellent outil. Toutefois, Microsoft ayant entreprit de conquérir le marché des outils de persistance avec ObjectSpaces, la bataille sera rude. Evaluant est une jeune société sans aucune mesure avec le géant de Redmond et il va lui falloir bien plus que des arguments techniques pour arriver à se démarquer sur un marché qui verra sûrement l'arrivée d'autres prétendants. Nous ne pouvons que vous conseiller de surveiller de près ce produit. N'hésitez pas à récupérer les sources de l'étude de cas afin de faire fonctionner l'application et juger ainsi par vous même.
Auteur : Sami Jaber
Copyright : DotNetGuru © 2002
Ressources (dernière MAJ : Juillet 2002)
La rumeur ObjectSpaces de Microsoft : dotnetguru.org
Evaluant : www.evaluant.com
"The first release of our .Net persistence tool, the Data Tier Modeler, is now online. Sebastien Ros" (Date : Juillet 2002)
Téléchargement du produit sur le site d'Evaluant
(dernière MAJ : Juillet 2002)
You can create an account and download it on our web site at www.evaluant.com.
This version is limited to 5 classes, and you can only generate compiled assemblies.
To upgrade to a higher version, please contact us
Interview de Sébastien Ros, Directeur technique et fondateur d'Evaluant
Je suis Fondateur et directeur
technique d'Evaluant et chargé des activités d'évolutions et de
développement du produit dans le cadre de
la veille technologique.
Concernant
le modèle de développement, la plus grande différence technique
réside dans le fait qu'il n'y a aucune phase d'enrichissement du code, pas dynamiquement. Le DTM utilise un modèle statique
(UML, XML, DDL) afin de générer chacune des entités de
l'application ainsi que "tout" le code de persistance. Les classes générées
dérivent de classes telles que DBObject, ou DBCollection, ... Le code
lié à chaque classe est aussi généré.
L'avantage évident de cette technique se situe dans la rapidité d'exécution, condition primordiale pour ce type de
Framework.
A l'opposé, cela pose une contrainte : la totalité de la classe est réécrite à chaque génération
et peut poser problème dans le cas où l'on souhaite implémenter de la logique métier. Mais nous résolvons le problème en
parsant le fichier source avant chaque re-génération afin de ré-implenter le code
inséré par l'utilisateur.
Chaque instance possède deux modes d'accès : Read/Write avec un mode Write par défaut pour la transparence du fonctionnement. Cela permet d'optimiser les accès aux bases pour le mode lecture seule ("lecture seule" en base
et non pas en mémoire !) pour faire du binding par exemple.
Concernant le mapping objet/relationnel, c'est la grande force du DTM à mon avis. Nous nous
interfaçons directement avec un modèle UML statique, depuis notre propre éditeur (développé en CSharp et
gratuit) mais aussi les éditeurs du marché (Rose, Together, ArgoUML) via le format
XMI.
Nous générons le mapping objet/relationnel en créant des scripts SQL de génération de base ou en nous interfaçant directement
avec la base. Ainsi nous sommes en mesure de répercuter les modifications des
diagrammes UML directement dans les tables.
Dans notre éditeur, une double vue UML/Tables est prévue, avec rétro-modélisation de la base en
diagrammes UML pouvant être enrichis ... Cela peut paraître complexe à
première vue, mais à l'usage, toute personne possédant des notions de mapping
y trouvera son compte. Cependant, nous imposons quelques règles pour chaque entité UML (interface,
hiérarchie des classes, cardinalités ...) afin de gérer dès à présent les relations
many-to-many, les classes d'associations ...
Par ailleurs, il est possible de générer autant d'implémentations qu'il
existe de SGBD, avec ou sans procédures stockées, et ce, tout en conservant la même interface.
Il suffit de faire référence à la bonne Assembly (DLL). Il est même
envisageable de changer dynamiquement de base entre les phases de développement et
d'installation finale chez un client.
Concernant la gestion des transactions, tout comme ObjectSpaces, nous proposons les transactions manuelles et automatiques. La gestion des accès concurrents est réalisé via un DataSet contenant les données.
Le langage d'interrogation utilisé est OPath, là encore, comme ObjectSpaces. Cependant, il est toujours possible de charger des instances d'objets à l'aide du langage SQL.
Une autre fonctionnalité intéressante est la possibilité de mettre en mémoire des collections d'objets complexes sans résoudre les dépendances contenues dans ces collections (Lazy loading des entités).
Enfin, concernant le cache d'objet, il est réalisé de sorte qu'il n'y ait qu'une seule instance du même objet dans chaque Thread d'application. Cela ne permet donc pas de disposer d'un cache distribué mais rien n'empêche l'utilisateur de créer ces propres interfaces .NET Remoting. Concernant une intégration plus poussée, nous sommes en train d'étudier cette solution dans le cadre d'un "serveur d'objets" par exemple. Il est aussi possible de libérer les instances manuellement, même si cette gestion est automatique.
Oui DTM sera payant. Il comprendra
en outre l'éditeur UML/Base de données. Le prix approximatif sera de moins de 1000€ par
licence, avec dégressivité dans le cadre d'une version complète comprenant la
génération Java et C#.
Nous pensons réaliser une version plus "légère" limitant le nombre de classes et les langages.
L'application propose aussi de développer des modèles de génération
personnalisés (doc, composants, ...) sur le principe d'Olymars
de Pascal Belaud.
Oui, via XMI.
Je ne pense pas que cette option soit judicieuse puisque nous fournissons déjà l'interface graphique appropriée, mais cela peut constituer un argument commercial. Cependant, ce n'est pas prévu à courte échéance.
Chaque chargement entre entité est en Lazy Binding. Chaque entité propre est en agressive binding, sauf dans le cas des collections. Cela fait partie des choses que nous pouvons implémenter via des paramètres de génération, mais qui ne le sont pas encore. Cette question, assez rarement posée, est un réel problème. En général, le chargement complet de l'ensemble des attributs d'une classe est la façon la plus optimisée.
Nous travaillons depuis plus d'un an sur l'implémentation d'un tel
Framework, et avons une idée concrète des avantages que cela représente. Je pense que les technologies objets avancent beaucoup plus
vite que les savoir-faire. Il est nécessaire de simplifier tout cela au travers
d'une standardisation. On le voit aujourd'hui avec le JDK et .Net qui simplifient la programmation sans la brider.
Donc Oui, mais JDO est à mon avis trop peu exploitable aujourd'hui car pas assez simple d'utilisation. Il serait dommage qu'une standardisation
vienne brider les efforts de simplification. Par contre cela reste une démarche intéressante.
Donc oui pour une spécification mais en suivant le principe de DTM ! ;-)
Merci beaucoup Sébastien Ros et bonne continuation !
Auteur : Propos recueillis par Sami Jaber
Copyright : DotNetGuru Ó 2002