26 janvier 2009

Entity Framework et les types complexes

Les types complexes font parti des fonctionnalités manquantes de l'éditeur de modèle EDM fourni avec Visual Studio 2008. Pour pouvoir en bénéficier, il est nécessaire de modifier les fichiers CSDL (schéma conceptuel) et MSL (schéma de liaison) ainsi que de modifier les classes d'entité.

Tout d'abord à quoi sert un type complexe ? Dans une entité, il peut être utile de séparer dans un nouveau type plusieurs variables cohérentes entre elles. Par exemple, la classe Client est composée des variables suivantes :
  • Id

  • Nom

  • Prenom

  • Telephone

  • Pays

  • Rue

  • CodePostal

  • Ville

Les 4 dernières variables sont en relation avec l'adresse du client. Il serait donc logique de les déplacer dans une classe appelée Adresse, d'où l'utilité du type complexe.

Dans Entity Framework, un type complexe ne contient aucun identifiant et il est forcément relié à une entité mère, Client pour notre exemple. Comme avec l'héritage "1 table -> plusieurs entités", c'est une autre manière de définir plusieurs entités pour une seule table.

Si vous souhaitez mettre en place un type complexe, vous serez obligé d'utiliser directement les fichiers CSDL, MSL et SSDL. L'éditeur de modèle EDM cache ses 3 fichiers dans l'assemblage du projet en tant que ressources embarquées. Pour les extraire, j'ai utilisé Reflector et l'addin FileDisassembler permettant d'extraire tous les fichiers composant un assemblage.

Une autre solution est de passer par l'utilitaire "EdmGen.exe" qui permet de générer les fichiers CSDL, MSL, SSDL. Cependant, vous perdez tout le bénéfice de définir graphiquement votre modèle via l'éditeur d'EDM (une erreur de frappe est vite arrivé dans un fichier XML …).

Pour l'exemple, je pars de la base de données utilisée dans mon article publié sur developpez.com. Vous trouverez les scripts SQL Serveur 2005 de la base dans l'archive, à la fin de ce billet.

En 1er lieu, voici le schéma logique (SSDL) :

<Schema Namespace="helloentityfxModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2005" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
  <EntityContainer Name="CustomModelStoreContainer">
    <EntitySet Name="Client" EntityType="helloentityfxModel.Store.Client" store:Type="Tables" Schema="dbo" />
  </EntityContainer>
  <EntityType Name="Client">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
    <Property Name="Nom" Type="varchar" MaxLength="50" />
    <Property Name="Prenom" Type="varchar" MaxLength="50" /&gt
    <Property Name="Pays" Type="varchar" MaxLength="50" />
    <Property Name="Rue" Type="varchar" MaxLength="100" />
    <Property Name="CodePostal" Type="char" MaxLength="5" />
    <Property Name="Telephone" Type="char" MaxLength="10" />
    <Property Name="Ville" Type="varchar" MaxLength="50" />
  </EntityType>
</Schema>

Pour ce fichier, rien de spécial à signaler si ce n'est la description de la table et des propriétés (colonnes) qui la composent. Aucune référence au type complexe n'apparait. Ceci est tout à fait normal puisque c'est le schéma logique faisant référence à la structure de la base de données.

Passons maintenant au schéma conceptuel (CSDL) :

<Schema Namespace="CustomModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
  <EntityContainer Name="Context">
    <EntitySet Name="Client" EntityType="CustomModel.Client" />
  </EntityContainer>
  <EntityType Name="Client">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Type="Int32" Nullable="false" />
    <Property Name="Nom" Type="String" MaxLength="50" Unicode="false" FixedLength="false" />
    <Property Name="Prenom" Type="String" MaxLength="50" Unicode="false" FixedLength="false" />
    <Property Name="Telephone" Type="String" MaxLength="10" Unicode="false" FixedLength="true" />
    <Property Name="Adresse" Type="Self.Adresse" Nullable="false" />
  </EntityType>
  <ComplexType Name="Adresse">
    <Property Name="Pays" Type="String" MaxLength="50" Unicode="false" FixedLength="false" />
    <Property Name="Rue" Type="String" MaxLength="100" Unicode="false" FixedLength="false" />
    <Property Name="CodePostal" Type="String" MaxLength="5" Unicode="false" FixedLength="true" />
    <Property Name="Ville" Type="String" MaxLength="50" Unicode="false" FixedLength="false" />
  </ComplexType>
</Schema>

Il comporte la définition de l'entité Client comprenant entre autre une propriété Adresse faisant référence au type complexe CustomModel.Adresse. Celui-ci est composé de 4 propriétés (Pays, Rue, CodePostal, Ville).

Pour finir, voici le schéma de liaison (MSL) :

<Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
  <EntityContainerMapping StorageEntityContainer="CustomModelStoreContainer" CdmEntityContainer="Context">
    <EntitySetMapping Name="Client">
      <EntityTypeMapping TypeName="CustomModel.Client">
        <MappingFragment StoreEntitySet="Client">
          <ScalarProperty Name="Id" ColumnName="Id" />
          <ScalarProperty Name="Nom" ColumnName="Nom" />
          <ScalarProperty Name="Prenom" ColumnName="Prenom" />
          <ScalarProperty Name="Telephone" ColumnName="Telephone" />
          <ComplexProperty Name="Adresse" TypeName="CustomModel.Adresse">
            <ScalarProperty Name="Pays" ColumnName="Pays" />
            <ScalarProperty Name="Rue" ColumnName="Rue" />
            <ScalarProperty Name="CodePostal" ColumnName="CodePostal" />
            <ScalarProperty Name="Ville" ColumnName="Ville" />
          </ComplexProperty>
        </MappingFragment>
      </EntityTypeMapping>
    </EntitySetMapping>
  </EntityContainerMapping>
</Mapping>

On y remarque le mapping entre l'entité Client et la table Client, ainsi qu'entre le type complexe CustomModel.Adresse et la table Client.

Passons maintenant à la définition des classes et du contexte. 2 choix sont possibles, soit :
  • Utiliser le custom tool fourni par Visual Studio 2008 ou l'outil "edmgen.exe" pour générer les classes et le contexte à partir du fichier CSDL.

  • Définir/Modifier à la main vos propres classes d'entité. Cette méthode est utile lorsque vous possédez déjà vos classes métier. Cela consiste à implémenter pour chaque entité les interfaces IPOCO d'Entity Framework et d'ajouter quelques attributs.

Pour faire simple, voici la démarche pour utiliser le Custom Tool "EntityModelCodeGenerator" :
  • Sélectionnez le schéma conceptuel (fichier CSDL) dans Visual Studio.

  • Dans la fenêtre de propriétés, définissez la propriété Custom Tool à "EntityModelCodeGenerator".

  • A chaque sauvegarde du fichier CSDL, un fichier contenant les classes d'entité et le contexte est généré.

Et voila, si tout s'est bien passé, Client possède maintenant une variable de type Adresse, qui est notre type complexe.

Vous trouverez dans l'archive ci-dessous le code source de l'article et les scripts SQL Serveur 2005 de la base de données.

1 commentaire:

Unknown a dit…

Bonjour,

Il est dommage que vous ayiez arrêté votre blog qui est fort intéressant !