Comme on était de vrais geeks devant un écran noir à fonte verte, avec du vi, il nous fallait trouver un moyen pour automatiser toute cette phase de code plus ou moins toujours la même. J'avais alors créé une petite grammaire (qu'on appellerait DSL maintenant) à base de create table, afin de générer les fichiers C ad-hoc pour chaque table contenant les champs et index souhaités. Pour développer ce genre de programme, un compilateur en gros, nous utilisions le couple Lex & Yacc pour interpréter notre pseudo-langage (les create table avec les champs typés, et les index) afin de générer le code C pour utiliser l'API C-ISAM.

Mais pourquoi tu nous racontes ta p'tite vie de geek Olive ? tout simplement que lorsque vous utilisez NHibernate, la phase mapping, création des classes peut être très rapidement rébarbative. NHModeler se propose de résoudre tout ça, pour nous, informaticiens feignants : à partir d'une grammaire à base d'entités (le modèle), de générer une multitudes de fichiers : le modèle (les class C# de nos entités), les .hbm (le mapping), le fichier SQL de création des tables, fichiers qu'on aura plus qu'à intégrer à notre projet. Cette génération s'apparente à du MDA (Model Driven Architecture), avec ses inconvénients et ses avantages.

NHModeler

NHModeler a besoin du SDK Oslo. Également, afin de lancer la compilation de votre DSL, il manque 2 assembly dans les binaires fournis : log4net et les Iesi.Collections, les 2 pourront être pris de vos binaires NHibernate.

Comme toujours, on souhaite représenter la relation suivante :

une relation many-to-many dans le langage NHibernate.

On déclare cette relation dans le langage NHModeler, contenu d'un fichier nommé respassoc.nhm :

NHModel 
{
Entity Responsable
{
Nom: string(30)
Prenom: string(30)
Assocs->Responsables: Assoc *
} IN Responsables
 
Entity Assoc
{
Libelle: string(30)
Description: string
Responsables<-Assocs: Responsable *
} IN Assocs
}

note : si l'on ne souhaite pas de modifications en cascade entre les 2 entités, il suffit d'ajouter le mot clé weak derrière les relations :

Assocs->Responsables: Assoc * weak
Responsables<-Assocs: Responsable * weak

il reste à exécuter la génération de code, de notre fichier respassoc.nhm qui contient notre description précédente, pour le dialect SQL Server 2005 :

NHModeller.Console -a:Model  -d:MsSql2005 -ddl:dbscript.sql -out:"c:\temp\mymodel" respassoc.nhm

on obtient l'ensemble utile à NHibernate, les classes, hbm et le script SQL de création de tables

NHModeler

dans le script de création nous avons bien entendu les 3 tables qui représentent notre relation : Responsables, Assocs et la table de jointure Assoc_Responsable

CREATE TABLE Responsables (
Id INT IDENTITY NOT NULL,
Nom NVARCHAR(30) NOT NULL,
Prenom NVARCHAR(30) NOT NULL,
PRIMARY KEY (Id)
);
 
CREATE TABLE Assoc_Responsable (
Responsable_Id INT NOT NULL,
Assoc_Id INT NOT NULL,
PRIMARY KEY (Assoc_Id, Responsable_Id)
);
 
CREATE TABLE Assocs (
Id INT IDENTITY NOT NULL,
Libelle NVARCHAR(30) NOT NULL,
Description NVARCHAR(255) NOT NULL,
PRIMARY KEY (Id)
);
ALTER TABLE Assoc_Responsable ADD constraint FKFF76651DD5384AF6 FOREIGN KEY (Assoc_Id) REFERENCES Assocs;
ALTER TABLE Assoc_Responsable ADD constraint FKFF76651D9647BEE3 FOREIGN KEY (Responsable_Id) REFERENCES Responsables;

et les *.hbm.xml :

<?xml version='1.0' ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
<class name="Responsable" table="Responsables">
<id name="Id" column="Id" access="field.camelcase-underscore">
<generator class="native" />
</id>
<property name="Nom" column="Nom" type="String" not-null="true" length="30" />
<property name="Prenom" column="Prenom" type="String" not-null="true" length="30" />
<set table="Assoc_Responsable" access="field.camelcase-underscore" name="Assocs" cascade="none">
<key column="Responsable_Id" />
<many-to-many class="Assoc" outer-join="true" column="Assoc_Id" />
</set>
</class>
</hibernate-mapping>
 
<?xml version='1.0' ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
<class name="Assoc" table="Assocs">
<id name="Id" column="Id" access="field.camelcase-underscore">
<generator class="native" />
</id>
<property name="Libelle" column="Libelle" type="String" not-null="true" length="30" />
<property name="Description" column="Description" type="String" not-null="true" />
<set table="Assoc_Responsable" access="field.camelcase-underscore" name="Responsables" cascade="none">
<key column="Assoc_Id" />
<many-to-many class="Responsable" outer-join="true" column="Responsable_Id" />
</set>
</class>
</hibernate-mapping>

La documentation est riche avec des exemples, le langage est assez complet et semble répondre aux principales fonctionnalités de NHibernate, ce projet vaut la peine de s'y arrêter, au moins pour se rappeller de bons souvenirs ;)

Petit défaut : pas de sources disponibles.