Introduction par l'exemple à Entity Framework 5 Code First


précédentsommairesuivant

VI. Etude de cas avec PostgreSQL 9.2.1

VI-A. Installation des outils

Les outils à installer sont les suivants :

  • le SGBD : [http://www.enterprisedb.com/products-services-training/pgdownload#windows] ;
  • un outil d'administration : EMS SQL Manager for PostgreSQL Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].

Dans les exemples qui suivent, l'utilisateur postgres a le mot de passe postgres .

Lançons PostgreSQL puis l'outil [SQL Manager Lite for PostgreSQL] avec lequel on va administrer le SGBD.

Image non disponible
  • en [1], nous lançons le SGBD PostgreSQL à partir des services Windows ;
  • en [2], le service est démarré ;

Nous lançons maintenant l'outil [SQL Manager Lite for MySQL] avec lequel on va administrer le SGBD [3].

Image non disponible
  • en [4], nous créons une nouvelle base ;
  • en [5], on indique le nom de la base ;
Image non disponible
  • en [5], on se connecte en tant que postgres / postgres ;
  • en [6], on donne quelques informations ;
  • en [7], on valide l'ordre SQL qui va être exécuté ;
Image non disponible
  • en [8], la base a été créée. Elle doit maintenant être enregistrée dans [EMS Manager]. Les informations sont bonnes. On fait [OK] ;
  • en [9], on s'y connecte ;
  • en [10], [EMS Manager] affiche la base, pour l'instant vide. On notera que les tables appartiendront à un schéma appelé public [11].

Nous allons maintenant connecter un projet VS 2012 à cette base.

VI-B. Création de la base à partir des entités

Nous commençons par dupliquer le dossier du projet [RdvMedecins-SqlServer-01] dans [RdvMedecins-PostgreSQL-01] [1] :

Image non disponible
  • en [2], dans VS 2012, nous supprimons le projet [RdvMedecins-SqlServer-01] de la solution ;
Image non disponible
  • en [3], le projet a été supprimé ;
  • en [4], on en rajoute un autre. Celui-ci est pris dans le dossier [RdvMedecins-PostgreSQL-01] que nous avons créé précédemment ;
Image non disponible
  • en [5], le projet chargé s'appelle [RdvMedecins-SqlServer-01] ;
  • en [6], on change son nom en [RdvMedecins-PostgreSQL-01]
Image non disponible
  • en [7], on rajoute un autre projet à la solution. Celui-ci est pris dans le dossier [RdvMedecins-SqlServer-01] du projet que nous avons supprimé de la solution précédemment ;
  • en [8], le projet [RdvMedecins-SqlServer-01] a réintégré la solution.

Le projet [RdvMedecins-PostgreSQL-01] est identique au projet [RdvMedecins-SqlServer-01]. Il nous faut faire quelques modifications. Dans [App.config], nous allons modifier la chaîne de connexion et le [DbProviderFactory] qu'on doit adapter à chaque SGBD.

[App.config]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<!-- chaîne de connexion sur la base -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- le factory provider -->
  <system.data>
    <DbProviderFactories>
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
    </DbProviderFactories>
  </system.data>
  • ligne 3 : l'utilisateur et son mot de passe ;
  • lignes 7-9 : le DbProviderFactory . La ligne 8 référence une DLL [ Npgsql ] que nous n'avons pas. On se la procure avec NuGet [1] :
Image non disponible
  • en [2], dans la zone de recherche on tape le mot clé postgresql  ;
  • en [3], on choisit le paquetage [Npgsql]. C'est un connecteur ADO.NET pour PostgreSQL ;
Image non disponible
  • en [4], deux références ont été ajoutées ;
  • en [5], dans [App.config], il faut mettre la version correcte de la DLL. On la trouve dans ses propriétés.

Dans le fichier [Entites.cs], il faut adapter le schéma des tables qui vont être générées :

 
Sélectionnez
  [Table("MEDECINS", Schema = "public")]
  public class Medecin : Personne
  {...}

  [Table("CLIENTS", Schema = "public")]
  public class Client : Personne
  {...}

  [Table("CRENEAUX", Schema = "public")]
  public class Creneau
  {...}

  [Table("RVS", Schema = "public")]
  public class Rv
  {...}

Nous avons vu précédemment lors de la création d'une base PostgreSQL, que les tables appartenaient à un schéma appelé public .

Nous configurons l'exécution du projet :

Image non disponible
  • en [1], on donne un autre nom à l'assembly qui va être généré ;
  • en [2], ainsi qu'un autre espace de noms par défaut ;
  • en [3], on désigne le programme à exécuter.

A ce stade, il n'y a pas d'erreurs de compilation. Exécutons le programme [CreateDB_01]. On obtient l'exception suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Exception non gérée : System.Data.MetadataException: Le schéma spécifié n'est pas valide. Erreurs :
(11,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(23,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(33,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(43,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
   à System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors
()
   ...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

On se rappelle avoir eu la même erreur avec MySQL et Oracle. C'est lié au type du champ Timestamp des entités. Nous faisons la même modification qu'avec Oracle. Dans les entités, nous remplaçons les trois lignes

 
Sélectionnez
1.
2.
3.
    [Column("TIMESTAMP")]
    [Timestamp]
    public byte[] Timestamp { get; set; }

par les suivantes :

 
Sélectionnez
1.
2.
3.
    [ConcurrencyCheck]
    [Column("VERSIONING")]
    public int? Versioning { get; set; }

On change le type de la colonne qui passe de byte[] à int? . Dans le SGBD, nous utiliserons des procédures stockées pour incrémenter cet entier d'une unité à chaque fois qu'une ligne sera insérée ou modifiée.

Nous faisons la modification précédente pour les quatre entités puis nous réexécutons l'application. Nous obtenons alors l'erreur suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
Exception non gérée : System.Data.DataException: An exception occurred while initializing the database. See the InnerException for details. ---> System.Data.ProviderIncompatibleException: DeleteDatabase n'est pas pris en charge par le fournisseur.
   à System.Data.Common.DbProviderServices.DbDeleteDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
   ...
   à System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

La ligne 1 indique que le connecteur ADO.NET de PostgreSQL n'est pas capable de supprimer la base existante. Exactement comme avec Oracle. Nous sommes amenés alors à construire la base de données [RDVMEDECINS-EF] à la main avec l'outil [EMS Manager for PostgreSQL]. Nous ne décrivons pas toutes les étapes mais simplement les plus importantes.

La base PostgreSQL sera la suivante :

Les tables

Image non disponible
  • en [1], ID est clé primaire de type serial . Ce type PostgreSQL est un entier généré automatiquement par le SGBD.
Image non disponible
Image non disponible
Image non disponible

Les différentes tables ont les clés primaires et étrangères qu'avaient ces mêmes tables dans les exemples précédents. Les clés étrangères ont l'attribut ON DELETE CASCADE.

Les séquences

Comme avec Oracle, nous avons créé ici des séquences. Ce sont des générateurs de nombres consécutifs. Il y en a 5 [1].

Image non disponible
  • en [2], nous voyons les propriétés de la séquence [CLIENTS_ID_SEQ]. Elle génère des nombres consécutifs de 1 en 1, à partir de 1 jusqu'à une valeur très grande.

Toutes les séquences sont bâties sur le même modèle.

  • [CLIENTS_ID_seq] sera utilisée pour générer la clé primaire de la table [CLIENTS] ;
  • [MEDECINS_ID_seq] sera utilisée pour générer la clé primaire de la table [MEDECINS] ;
  • [CRENEAUX_ID_seq] sera utilisée pour générer la clé primaire de la table [CRENEAUX] ;
  • [RVS_ID_seq] sera utilisée pour générer la clé primaire de la table [RVS] ;
  • [sequence_versions] sera utilisée pour générer les valeurs des colonnes [VERSIONING] de toutes les tables.

Les triggers

Un trigger est une procédure exécutée par le SGBD avant ou après un événement (Insertion, Modification, Suppression) dans une table. Nous en avons 4 [1] :

Image non disponible

Regardons le code DDL du trigger [CLIENTS_tr] qui alimente la colonne [VERSIONING] de la table [CLIENTS] :

 
Sélectionnez
1.
2.
3.
4.
CREATE TRIGGER "CLIENTS_tr"
  BEFORE INSERT OR UPDATE 
  ON public."CLIENTS" FOR EACH ROW 
EXECUTE PROCEDURE public.trigger_versions();
  • lignes 1-3 : avant chaque opération INSERT ou UPDATE sur la table [CLIENTS] ;
  • ligne 4 : la procédure [public.trigger_versions()] est exécutée.

La procédure [public.trigger_versions()] est la suivante :

 
Sélectionnez
1.
2.
3.
4.
BEGIN
NEW."VERSIONING":=nextval('sequence_versions');
return NEW;
END
  • ligne 2 : NEW représente la ligne qui va être insérée ou modifiée. NEW. "VERSIONING " est la colonne [VERSIONING] de cette ligne. On lui affecte la valeur suivante du générateur de nombres " sequence_versions ". Ainsi la colonne ["VERSIONING"] change à chaque INSERT / UPDATE fait sur la table [CLIENTS].

Les triggers [MEDECINS_tr, CRENEAUX_tr, RVS_tr] sont analogues. Les quatre colonnes ["VERSIONING"] tirent leur valeurs de la même séquence.

Le script de génération des tables de la base PostgreSQL [RDVMEDECINS-EF] a été placé dans le dossier [RdvMedecins / databases / postgreSQL]. Le lecteur pourra le charger et l'exécuter pour créer ses tables.

Ceci fait, les différents programmes du projet peuvent être exécutés. Ils donnent les mêmes résultats qu'avec SQL Server sauf pour le programme [ModifyDetachedEntities] qui plante pour la même raison qu'il avait planté avec Oracle. On résoud le problème de la même façon. Il suffit de copier le programme [ModifyDetachedEntities] du projet [RdvMedecins-Oracle-01] dans le projet [RdvMedecins-PostgreSQL-01].

Le programme [LazyEagerLoading] plante avec l'exception suivante :

 
Sélectionnez
1.
2.
3.
4.
Exception non gérée : System.Data.EntityCommandExecutionException: Une erreur s'est produite lors de l'exécution de la définition de la commande. Pour plus de détails, consultez l'exception interne. ---> Npgsql.NpgsqlException: ERREUR: 42601: erreur de syntaxe sur ou près de « LEFT »
   à Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__a.MoveNext()
   ...
   à RdvMedecins_01.LazyEagerLoading.Main(String[] args) dans d:\data\istia-1213\c#\dvp\Entity Framework\RdvMedecins\RdvMedecins-PostgreSQL-01\LazyEagerLoading.cs:ligne 23

Le code erroné est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
      using (var context = new RdvMedecinsContext())
      {
        // creneau  0
        creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
}

Ligne n° 1 de l'exception, l'erreur signalée fait penser à une jointure car LEFT est un mot clé de la jointure. Parce que la ligne 4 du code ci-dessus, demande le chargement immédiat de la dépendance [Medecin] d'une entité [Creneau], EF a fait une jointure entre les tables [CRENEAUX] et [MEDECINS]. Mais il semble que le connecteur ADO.NET ait généré un ordre SQL incorrect. Nous réécrivons le code de la façon suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
      using (var context = new RdvMedecinsContext())
      {
        // creneau  0
        creneau = context.Creneaux.Find(idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
        // on force le chargement du médecin associé
        // c'est possible car on est encore dans un contexte ouvert
        Medecin medecin = creneau.Medecin;
}
  • ligne 4 : on va chercher le créneau sans jointure ;
  • ligne 8 : on récupère la dépendance manquante.

Ca marche. De nouveau on constate que le changement de SGBD a un impact sur le code. En fait ce n'est pas le SGBD qui est en cause ici mais son connecteur ADO.NET.

VI-C. Architecture multicouche s'appuyant sur EF 5

Nous revenons à notre étude de cas décrite au paragraphe , page .

Image non disponible

Nous allons commencer par construire la couche [DAO] d'accès aux données. Pour ce faire, nous dupliquons le projet console VS 2012 [RdvMedecins-SqlServer-02] dans [RdvMedecins-PostgreSQL-02] [1] :

Image non disponible
  • en [2], on supprime le projet [RdvMedecins-SqlServer-02] ;
Image non disponible
  • en [3], on ajoute un projet existant à la solution. On le prend dans le dossier [RdvMedecins-PostgreSQL-02] qui vient d'être créé ;
  • en [4], le nouveau projet porte le nom de celui qui a été supprimé. On va changer son nom ;
Image non disponible
  • en [5], on a changé le nom du projet ;
  • en [6], on modifie certaines de ses propriétés, comme ici le nom de l'assembly ;
  • en [7], le dossier [Models] est supprimé pour être remplacé par le dossier [Models] du projet [RdvMedecins-PostgreSQL-01]. En effet, les deux projets partagent les mêmes modèles.
Image non disponible
  • en [8], les références actuelles du projet ;
  • en [9], on y a ajouté le connecteur ADO.NET de PostgreSQL avec l'outil NuGet.

Dans le fichier [App.config], on remplace les informations de la base SQL Server par celles de la base PostgreSQL. On les trouve dans le fichier [App.config] du projet [RdvMedecins-PostgreSQL-01] :

[App.config]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
  <!-- chaîne de connexion sur la base -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- le factory provider -->
  <system.data>
    <DbProviderFactories>
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
    </DbProviderFactories>
</system.data>

Les objets gérés par Spring changent également. Actuellement on a :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
  <!-- configuration Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
    </objects>
</spring>

La ligne 7 référence l'assembly du projet [ RdvMedecins-SqlServer-02 ]. L'assembly est désormais [ RdvMedecins-PostgreSQL-02 ].

Ceci fait, nous sommes prêts à exécuter le test de la couche [DAO]. Il faut auparavant prendre soin de remplir la base (programme [Fill] du projet [ RdvMedecins-PostgreSQL-01 ]. Le programme de test plante avec l'exception suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
2012/10/12 13:56:27:188 [INFO]  Spring.Context.Support.XmlApplicationContext - A
pplicationContext Refresh: Completed
Liste des clients :
Client [47,Mr,Jules,Martin,468]
Client [48,Mme,Christine,German,469]
Client [49,Mr,Jules,Jacquard,470]
Client [50,Melle,Brigitte,Bistrou,471]
Liste des médecins :
Medecin [42,Mme,Marie,Pelissier,472]
Medecin [43,Mr,Jacques,Bromard,497]
Medecin [44,Mr,Philippe,Jandot,510]
Medecin [45,Melle,Justine,Jacquemot,511]
L'erreur suivante s'est produite : RdvMedecinsException[3,GetCreneauxMedecin,Une erreur s'est produite lors de l'exécution de la définition de la commande. Pour plus de détails, consultez l'exception interne.]

Ligne 13, le message indique que l'erreur s'est produite dans la méthode [GetCreneauxMedecin] de la couche [DAO]. Celle-ci est la suivante :

[GetCreneauxMedecin]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
    // liste des créneaux horaires d'un médecin donné
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // liste des créneaux
      try
      {
        // ouverture contexte de persistance
        using (var context = new RdvMedecinsContext())
        {
          // on récupère le médecin avec ses créneaux
          Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
          // on retourne la liste des créneaux du médecin
          return medecin.Creneaux.ToList<Creneau>();
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

Ligne 11, on reconnaît le mot clé Include qui a déjà fait planter un précédent programme. Le code précédent peut être remplacé par le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
    // liste des créneaux horaires d'un médecin donné
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // liste des créneaux
      try
      {
        // ouverture contexte de persistance
        using (var context = new RdvMedecinsContext())
        {
          // on retourne la liste des créneaux du médecin
          return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>(); 
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

Le nouveau code paraît même plus cohérent que l'ancien. Toujours est-il que cette fois-ci le programme de test passe.

Nous créons la DLL du projet comme il a été fait pour le projet [ RdvMedecins-SqlServer-02 ] et nous rassemblons l'ensemble des DLL du projet dans un dossier [lib] créé dans [ RdvMedecins-PostgreSQL-02 ]. Ce seront les références du projet web [ RdvMedecins-PostgreSQL-03 ] qui va suivre.

Image non disponible

Nous sommes désormais prêts pour construire la couche [ASP.NET] de notre aplication :

Image non disponible

Nous allons partir du projet [RdvMedecins-SqlServer-03]. Nous dupliquons le dossier de ce projet dans [RdvMedecins-PostgreSQL-03] [1] :

Image non disponible
  • en [2], avec VS 2012 Express pour le web, nous ouvrons la solution du dossier [RdvMedecins-PostgreSQL-03] ;
  • en [3], nous changeons et le nom de la solution et le nom du projet ;
Image non disponible
  • en [4], les références actuelles du projet ;
  • en [5], on les supprime ;
  • en [6], pour les remplacer par des références sur les DLL que nous venons de stocker dans un dossier [lib] du projet [RdvMedecins-PostgreSQL-02].

Il ne nous reste plus qu'à modifier le fichier [Web.config] On remplace son contenu actuel par le contenu du fichier [App.config] du projet [RdvMedecins-PostgreSQL-02]. Ceci fait, on exécute le projet web. Il marche. On n'oubliera pas de remplir la base avant d'exécuter l'application web.


précédentsommairesuivant

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

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2012 Serge Tahé. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.