IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Mapping Objet/Relationnel : Test de Persistent Datasets

Cet article a pour but de vous faire découvrir les fonctionnalités de Persistent Datasets, un outil de mapping objet/relationnel.

N'hésitez pas à commenter cet article ! Commentez Donner une note à l´article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Qu'est-ce que le Mapping Objet/Relationnel

Un logiciel de Mapping Objet / Relationnel est une couche de persistance connectant les objets d'un système orienté objet à des données stockées dans une base de données relationnelle.
Pour simplifier, vous pouvez vous dire que le Mapping Objet/Relationnel consiste à modéliser, de façon objet, une base de données. Ainsi, les tables de la base de données deviennent des classes, avec une liste de propriétés qui correspondent aux colonnes de la table.

Une couche de persistance, c'est en quelque sorte une surcouche à votre projet, qui se chargera de représenter vos tables en classes.
C'est grâce à cette couche de persistance que vous allez pouvoir appeler des méthodes de votre classe, qui seront en fait des méthodes agissant directement sur votre base de données (méthodes Save, Update, etc.).

Vous avez donc la possibilité de créer cette couche de persistance vous-même : pour chaque table de la base de données, vous créez la classe associée (en prenant garde de bien respecter les clés primaires, les identifiants autogénérés, etc..). Ce travail n'est pas impossible, mais long et fastidieux.
Ou bien vous pouvez utiliser un logiciel qui créera pour vous, automatiquement, cette couche de persistance (cas le plus courant).

II. Pourquoi le Mapping Objet/Relationnel

Vous pouvez vous demander quel est l'intérêt du Mapping Objet/Relationnel ?
Si vous voulez faire abstraction de toute la partie SQL, et ne travailler qu'avec des objets (à proprement parler), le Mapping O/R est fait pour vous.
Une autre possibilité, du Mapping O/R, qui pourra vous séduire : si vous voulez mettre à jour, de façon complètement automatique, votre base de données, vous n'avez qu'à appeler une méthode Update (ou Save) sur l'un de vos objets. Grâce au Maping, la connexion à la base de données, les requêtes SQL et la fermeture de la connexion seront effectuées sans que vous ayez besoin d'écrire une seule ligne de SQL !

Cependant, prenez garde : le Mapping Object/Relationnel est tout de même critiquable d'un certain point de vue. En effet, en Programmation Orientée Object (POO), un objet n'est pas toujours représenté par une table d'une base de données : le Mapping O/R n'est donc pas une solution miracle.

Cet article a pour vocation de vous donner un aperçu des possibilités du logiciel Persistent Dataset.

III. Le Mapping Objet/Relationnel avec Persistent Dataset

Après avoir terminé l'installation du logiciel, rien ne semble, à priori, avoir changé dans votre Visual Studio. Commencez par vérifier que l'addin est bien chargé.
Pour cela, cliquez sur le menu « Tools », puis « Add-ins Manager », et là, vérifiez que Persistent Datasets est bien sélectionné :

Vérification du chargement de l'Addin


Affichez maintenant la barre d'outils de Persistent Datasets : faites un clic droit sur la barre à outils de Visual Studio et choisissez « Persistent Datasets » :

Barre d'outils Persistent Datasets


Maintenant que l'installation est terminée et que vous avez fini le paramétrage de Visual Studio, voyons comment utiliser les fonctionnalités du produit.

III-A. Avantages de Persistent Datasets

Persistent Datasets possède de nombreux avantages. Je ne vais pas vous en faire la liste détaillée, mais notez ceux-ci qui sont, pour moi, les plus importants :

  • support des Generics (.NET 2.0) ;
  • support des types Nullable ;
  • les résultats d'une requête peuvent être récupérés directement depuis la base de données, ou bien stockés dans des Dataset fortement typés ;
  • persistent Datasets utilise la même syntaxe que DLINQ (Data Language Integrated Queries). Voir ici pour plus d'informations sur DLINQ.

III-B. Ajout de la couche de persistance

Faîtes un clic droit que le nom de votre projet, choisissez « Add » => « New Item » et sélectionnez « Persistent Layer » :

Ajout de la couche de persistance


C'est à cet endroit-là que vous allez pouvoir générer votre couche de persistance, ainsi que tous les objets du domaine, etc.
« Mapping Schema » vous permettra de paramétrer la chaine de connexion à la base de données, et de spécifier des conventions de nommage :

Mapping Schema


Positionnez-vous alors sur « DB Objects » puis cliquez sur « Add » pour ajouter les tables de la base de données sur lesquelles vous souhaitez travailler :

Ajout des tables


Une fois les tables ajoutées, vous pouvez utiliser le propertyGrid situé sur la droite pour modifier les paramètres de génération d'une ou plusieurs tables.
« Typed Objects » vous permettra également de modifier certaines options, telles que les conventions de nommage des objets du domaine, des objets d'accès aux données, etc.

Paramétrage divers


Cliquez alors sur « Generate Persistent Layer » pour générer votre couche de persistance et voir les objets du domaine (et autres classes importantes) se générer automatiquement :

Génération de la couche de persistance


La liste des fichiers composant votre couche de persistance est ajoutée à votre projet :

Liste des fichiers de la couche de persistance

III-C. Génération du Framework d'initialisation

Maintenant que votre couche de persistance est générée, il va vous falloir créer le framework qui servira à activer la persistance. Pour cela, rien de plus simple : dans votre code, placez ce bout de code :

Activation de la persistance
Sélectionnez
//Instructs framework to ativate persistentce for mapping schema
MappingSchemaFactory.Add(Assembly.GetExecutingAssembly(), Assembly.GetExecutingAssembly(),
 "ClientApp.NorthwindPersistentLayer.xsd");
 
//Initialize Default DataBroker.
// Votre chaine de connexion
string connectionString = @"Data Source=.\SQL2005; Initial Catalog=Northwind;User Id=sa; Password=";
DataBroker.DefaultDataBroker = new DataBroker(new SqlDataProvider(), connectionString, null);
 
//Enable SQL Trace
DataBroker.DefaultDataBroker.EnableTraceSql = true;


Il y a trois choses que vous allez devoir changer pour que votre code soit correct :

  • le nom du Dataset : ClientApp.NorthwindPersistentLayer.xsd est le nom du Dataset généré par la couche de persistance. Vous allez devoir le remplacer par le nom de votre Dataset (en effet, NorthwindPersistentLayer n'est sans doute pas le nom que vous avez donné à votre couche de persistance). Attention, ce nom est composé de la sorte : namespace.nom_du_dataset.xsd ;
  • la chaine de connexion : en effet, c'est à vous d'adapter la chaine de connexion de votre code ;
  • le provider SQL à utiliser : si vous utilisez une base de données Oracle, vous allez devoir remplacer SqlDataProvider par OracleDataProvider.


Pensez également à rajouter les clauses « using » (ou « Import », si vous faites du VB.NET) nécessaires :

Ajout des using
Sélectionnez
using LastComponent.PersistentDataSets.Framework;
using LastComponent.PersistentDataSets.Framework.Data.SqlServer;
using LastComponent.PersistentDataSets.Framework.Schema;


Une fois que cela est fait, voyons les différentes opérations qu'il nous est possible de réaliser sur la base de données.

III-D. Sélection de données

Une fois que vous êtes arrivé jusque là, Visual Studio 2005 vous a généré un composant, le modèle de la couche de persistance. Comme tous les composants, celui-ci ce trouve dans la boîte à outils, et il ne vous reste plus qu'à le glisser/déposer pour pouvoir l'utiliser dans votre projet :

Composant de la couche de persistance


C'est grâce à ce modèle que vous allez pouvoir avoir accès à votre base de données.

En effet, si vous voulez faire un simple SELECT de la table Customers de votre base de données, rien de plus simple : il vous suffit d'utiliser les classes de type XXXQuery, où XXX représente la table que vous souhaitez interroger. Voici un exemple :

Requête sur la base
Sélectionnez
CustomerQuery listClients = new CustomerQuery();


Ce bout de code me sert à récupérer toutes les lignes de la table Customers. Voici d'ailleurs la requête SQL exécutée pour récupérer cette liste (cette requête est visible grâce au paramètre EnableTraceSql = true, que vous avez mis lors de l'initialisation du framework) :

 
Sélectionnez
SELECT A0.[CustomerID], A0.[CompanyName], A0.[ContactName], A0.[ContactTitle], A0.[Address], A0.[City], A0.[Region],
A0.[PostalCode], A0.[Country], A0.[Phone], A0.[Fax]
FROM [Northwind].[dbo].[Customers] A0


Avec une ligne de code, vous avez réussi à écrire l'équivalent d'une ligne de SQL, sans pour autant connaitre le SQL ! Magique non :)

Maintenant que vous avez ce jeu de résultat, il faut bien en faire quelque chose. Dans notre cas, nous allons nous en servir pour alimenter une GridView.
Pour cela, il vous faut appeler la méthode Fill de la table, du modèle de données, que vous voulez remplir. Concrètement, voici comment cela se caractérise, par le code :

Remplissage du modèle de données
Sélectionnez
// Requête sur la table Customers
CustomerQuery listClients = new CustomerQuery();
 
// Remplissage de la table Customers du modèle de données
northwind.Customers.Fill(listClients);


Et c'est terminé ! Il ne vous reste plus qu'à associer la propriété northwind.Customers à la propriété DataSource de votre DataGridView, et à observer le résultat :

Initialisation de la source de données
Sélectionnez
cbClientList.DataSource = northwind.Customers;


Ce qui donne en image :

Affichage des Customers


Simple non ?! Voici le code source complet de cet exemple :

Code source complet
Sélectionnez
using (TransactionContext ts = new TransactionContext(TransactionOption.Required))
{
    // Requête sur la table Customers
    CustomerQuery listClients = new CustomerQuery();
 
    // Remplissage de la table Customers du modèle de données
    northwind.Customers.Fill(listClients);
 
    cbClientList.DataSource = northwind.Customers;
    cbClientList.DisplayMember = northwind.Customers.ContactName.SourceColumn;
    cbClientList.ValueMember = northwind.Customers.CustomerID.SourceColumn;
}

Une chose importante à savoir et à garder à l'esprit : À chaque fois que vous faites un accès à votre base de données, vous devez utiliser une transaction, afin de prévenir toute erreur. C'est pourquoi le code précédent est inclus dans la directive using suivante : TransactionContext ts = new TransactionContext(TransactionOption.Required)

III-E. Sélection de données avec critères

Une autre possibilité de Persistent Datasets : faire une sélection de données, en utilisant des critères de choix. Voici un exemple, où on ne demande que la liste des Customers, dont le ContactName contient la lettre A :

Sélection avec critère
Sélectionnez
using (new TransactionContext(TransactionOption.Required))
{
    // On interroge la table Customers
    CustomerQuery listClients = new CustomerQuery();
 
    // On en sélectionne que ceux dont le ContactName contient la lettre A
    listClients.Criteria = listClients.ContactName.Like("%A%");
 
    northwind.Customers.Fill(listClients);
 
    cbClientList.DataSource = northwind.Customers;
    cbClientList.DisplayMember = northwind.Customers.ContactName.SourceColumn;
    cbClientList.ValueMember = northwind.Customers.CustomerID.SourceColumn;
}


Voici d'ailleurs la requête SQL correspondante à ce code :

 
Sélectionnez
SELECT A0.[CustomerID], A0.[CompanyName], A0.[ContactName], A0.[ContactTitle], A0.[Address], A0.[City], A0.[Region],
A0.[PostalCode], A0.[Country], A0.[Phone], A0.[Fax]
FROM [Northwind].[dbo].[Customers] A0
WHERE (A0.[ContactName] LIKE '%A%')

III-F. Sélection de données avec critères multiples

Bien sûr, vous pouvez tout à fait multiplier les critères de sélection ! Voici d'ailleurs un exemple :

Multiplication des critères de recherches
Sélectionnez
using (new TransactionContext(TransactionOption.Required))
{
    // On interroge la table Customers
    CustomerQuery listClients = new CustomerQuery();
 
    // On en sélectionne que ceux dont le ContactName contient la lettre A
    listClients.Criteria = listClients.ContactName.Like("%A%") &&
    listClients.Orders.OrderDate.Between(new DateTime(1982, 06, 28), DateTime.Now);
 
    northwind.Customers.Fill(listClients);
 
    cbClientList.DataSource = northwind.Customers;
    cbClientList.DisplayMember = northwind.Customers.ContactName.SourceColumn;
    cbClientList.ValueMember = northwind.Customers.CustomerID.SourceColumn;
}


Et la requête SQL générée :

 
Sélectionnez
SELECT A0.[CustomerID], A0.[CompanyName], A0.[ContactName], A0.[ContactTitle], A0.[Address], A0.[City], A0.[Region],
A0.[PostalCode], A0.[Country], A0.[Phone], A0.[Fax]
FROM [Northwind].[dbo].[Customers] A0
    INNER JOIN [Northwind].[dbo].[Orders] A1 ON (A0.[CustomerID] = A1.[CustomerID])
WHERE ((A0.[ContactName] LIKE '%A%') AND (A1.[OrderDate] BETWEEN '28/06/1982' AND '11/02/2006'))


On se rend tout de suite compte que cette requête SQL est plus complexe que les autres et pourtant, le code qui l'a généré est on ne peut plus simple à écrire et comprendre !
On appréciera surtout, dans le cas de la sélection de données avec des critères multiples, la syntaxe du code très particulière (mais agréable) inspirée de LINQ.

III-G. Sélection de données avec clauses (ORDER BY, GROUP BY, etc…)

Si vous avez besoin de faire une requête SQL avec une clause (type ORDER BY; GROUP BY, etc.), sachez que Persistent Datasets vous permet de le faire toujours très simplement.
Voyons cela en exemple :

Sélection de données avec clause
Sélectionnez
using (new TransactionContext(TransactionOption.Required))
{
    // On interroge la table Customers
    CustomerQuery listClients = new CustomerQuery();
 
    // On en sélectionne que ceux dont le ContactName contient la lettre A
    listClients.Criteria = listClients.ContactName.Like("%A%");
    listClients.OrderByList.Add(listClients.ContactName, true);
 
    northwind.Customers.Fill(listClients);
 
    cbClientList.DataSource = northwind.Customers;
    cbClientList.DisplayMember = northwind.Customers.ContactName.SourceColumn;
    cbClientList.ValueMember = northwind.Customers.CustomerID.SourceColumn;
}


Ici, on voit bien que la ligne : listClients.OrderByList.Add(listClients.ContactName, true); a été ajoutée et on devine facilement qu'elle nous sert à ajouter une clause de type ORDER BY.
OrderByList est en effet une collection de clause ORDER BY : on se contente donc d'ajouter, à cette collection, un élément. Dans l'exemple ci-dessus, j'ai trié par nom, et dans l'ordre alphabétique, le jeu de résultat retourné par ma requête.

Le résultat en image :

Résultats ordonés


Là encore, une requête SQL est générée :

 
Sélectionnez
SELECT A0.[CustomerID], A0.[CompanyName], A0.[ContactName], A0.[ContactTitle], A0.[Address], A0.[City], A0.[Region],
A0.[PostalCode], A0.[Country], A0.[Phone], A0.[Fax]
FROM [Northwind].[dbo].[Customers] A0
ORDER BY A0.[ContactName] ASC


Je ne vous montrerais que cet exemple, mais sachez que toutes les autres clauses SQL sont réalisables.

III-H. Sélection d'une ligne de la base de données

Si vous ne voulez charger qu'une seule ligne de la base de données, et ne pas faire l'équivalent d'un SELECT *, sachez que là encore, vous pouvez le faire. Par exemple, voyons comment nous pouvons récupérer uniquement l'enregistrement numéro 10248 de la table Orders de notre base de données :

Chargement unique
Sélectionnez
using (new TransactionContext(TransactionOption.RequiresNew))
{
    // Chargement de la commande n°10248
    Order order = northwind.Orders.Fill(10248);
 
    MessageBox.Show(
        String.Format("Nom de livraison: {0}{1}Adresse de livraison: {2}", order.ShipName,
            Environment.NewLine, order.ShipAddress));
}


Le résultat apparait dans une boîte de dialogue :

Affichage d'une ligne


Pour être sûr que les résultats ne soient pas erronés, regardons dans la base de données :

Résultat dans la base de données


Si l'on regarde la requête SQL qui a été exécutée, on s'aperçoit qu'il ne s'agit que d'un simple SELECT avec un WHERE :

 
Sélectionnez
SELECT A0.[OrderID], A0.[CustomerID], A0.[EmployeeID], A0.[OrderDate], A0.[RequiredDate], A0.[ShippedDate], A0.[ShipVia],
A0.[Freight], A0.[ShipName], A0.[ShipAddress],
A0.[ShipCity], A0.[ShipRegion], A0.[ShipPostalCode], A0.[ShipCountry]
FROM [Northwind].[dbo].[Orders] A0
WHERE (A0.[OrderID] = 10248)

III-I. Ajouts d'enregistrements dans la base de données

Pour ajouter des enregistrements à la base de données, rien de plus simple : il vous suffit simplement d'appeler la méthode AddNew de la table de votre modèle de persistance.

Ajout d'enregistrement
Sélectionnez
using (TransactionContext ts = new TransactionContext(TransactionOption.RequiresNew))
{
    // Ajout d'une ligne dans la table Employees
    northwind.Employees.AddNew("LEBRUN", "Thomas");
 
    // On met à jour la base de données
    northwind.Employees.Update();
 
    // Validation de la transaction
    ts.Commit();
}
Ajout d'un enregistrement


Une fois encore, la requête SQL autogénérée par Persistent Datasets peut être visualisée :

 
Sélectionnez
DECLARE @scope_identity int;
INSERT INTO [Northwind].[dbo].[Employees]([LastName], [FirstName], [Title], [TitleOfCourtesy], [BirthDate], [HireDate],
[Address], [City], [Region],
[PostalCode], [Country], [HomePhone], [Extension], [Photo], [Notes], [ReportsTo], [PhotoPath]) 
VALUES('LEBRUN', 'Thomas', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
SELECT @scope_identity = SCOPE_IDENTITY();
SELECT @scope_identity;


On voit même que Persistent Datasets est capable de déclarer des paramètres, que vous pourrez réutiliser dans votre code pour afficher, par exemple, le numéro de l'employé, etc.

III-J. Modifications d'enregistrements dans la base de données

Pour modifier un enregistrement, la procédure est simple :

  • récupérez l'enregistrement dont vous voulez modifier une valeur ;
  • appliquez les modifications que vous voulez ;
  • appeler la méthode Update ;
  • appelez la méthode Commit de votre transaction.


Voyons cela avec un exemple :

Modification d'un enregistrement
Sélectionnez
using (TransactionContext ts = new TransactionContext(TransactionOption.RequiresNew))
{
    // Une requête sur la table Employees
    EmployeeQuery query = new EmployeeQuery();
    query.Criteria = query.FirstName == "Thomas" && query.LastName == "LEBRUN";
 
    Employee [] myself = northwind.Employees.Fill(query);
 
    myself[0].Title = "Monsieur";
 
    myself[0].Update();
 
    ts.Commit();
}


Vous devez maintenant (tout du moins, je l'espère), comprendre ce code : on fait une requête sur la table Employees et on récupère toutes les informations à propos de l'employé Thomas LEBRUN.
Ensuite, on modifie son titre et on met à jour la base de données (la preuve, en image) :

Mise à jour d'un enregistrement


Obsevons les requêtes SQL qui ont été générées par notre couche d'abstraction :

 
Sélectionnez
SELECT A0.[EmployeeID], A0.[LastName], A0.[FirstName], A0.[Title], A0.[TitleOfCourtesy], A0.[BirthDate], A0.[HireDate],
A0.[Address], A0.[City], A0.[Region], A0.[PostalCode],
A0.[Country], A0.[HomePhone], A0.[Extension], A0.[Photo], A0.[Notes], A0.[ReportsTo], A0.[PhotoPath]
FROM [Northwind].[dbo].[Employees] A0
WHERE ((A0.[FirstName] = 'Thomas') AND (A0.[LastName] = 'LEBRUN'))
 
UPDATE [Northwind].[dbo].[Employees] SET [Title] = 'Monsieur' 
WHERE [EmployeeID] = 11;


Il y a deux requêtes :

  • la première sert à récupérer l'ensemble des informations de l'employé Thomas LEBRUN ;
  • la deuxième sert à mettre à jour son champ Title.


Notez que Persistent Datasets sait détecter et utiliser les clés primaires, et cela sans que vous ayez besoin de faire une modification.

III-K. Suppression d'enregistrements dans la base de données

La suppression d'un enregistrement est très similaire à la mise à jour :

Suppression d'un enregistrement
Sélectionnez
using (TransactionContext ts = new TransactionContext(TransactionOption.RequiresNew))
{
    // Une requête sur la table Employees
    EmployeeQuery query = new EmployeeQuery();
    query.Criteria = query.FirstName == "Thomas" && query.LastName == "LEBRUN";
 
    Employee[] myself = northwind.Employees.Fill(query);
 
    // On marque l'élément comme bon pour supprimer
    myself[0].Delete();
 
    myself[0].Update();
 
    ts.Commit();
}


Je ne prends pas la peine d'expliquer le code, celui-ci étant simple à comprendre. Mais n'hésitez pas à me contacter si vous avez des questions.

Jetons un rapide coup d'œil sur les requêtes SQL qui ont été générées :

 
Sélectionnez
SELECT A0.[EmployeeID], A0.[LastName], A0.[FirstName], A0.[Title], A0.[TitleOfCourtesy], A0.[BirthDate], A0.[HireDate],
A0.[Address], A0.[City], A0.[Region], A0.[PostalCode],
A0.[Country], A0.[HomePhone], A0.[Extension], A0.[Photo], A0.[Notes], A0.[ReportsTo], A0.[PhotoPath]
FROM [Northwind].[dbo].[Employees] A0
WHERE ((A0.[FirstName] = 'Thomas') AND (A0.[LastName] = 'LEBRUN'))
 
DELETE FROM [Northwind].[dbo].[Employees] 
WHERE [EmployeeID] = 11;


On notera tout de même la possibilité d'effacer, d'un seul coup, toutes les lignes d'une table grâce à la méthode DeleteAll :

Suppression de tous les enregistrements
Sélectionnez
using (TransactionContext ts = new TransactionContext(TransactionOption.RequiresNew))
{
    EmployeeQuery query = new EmployeeQuery();
 
    northwind.Employees.Fill(query);
 
    northwind.Employees.DeleteAll();
 
    northwind.Employees.Update();
 
    ts.Commit();
}

IV. Conclusions

Tout au long de cet article, j'ai tenté de vous faire découvrir l'étendue des possibilités de Persistent Datasets. Bien entendu, je n'ai pas pu tout couvrir, tellement le produit est complexe et intéressant.
J'espère cependant qu'il aura réussi à vous séduire et à vous faire comprendre que, même si vous ne connaissez pas (ou mal) le langage SQL, vous pouvez tout de même réaliser des applications complexes et bien architecturées.

V. Liens

VI. Téléchargements

Le projet d'exemple : Visual Studio 2005
Version PDF de l'article : Article PDF

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Copyright © 2006 LEBRUN Thomas. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.