Matthieu Mezil
Les bases d'Entity Data Model
Avec EDM, l'Entity Framework permet d'assurer l'indépendance entre les entités et le modèle de persistance et authorise un mapping très puissant
Par Matthieu Mezil publié le 24/03/2008 à 11:37
 
Dans un modèle relationnel, pour représenter une relation n->n, il faut passer par une table intermédiaire :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/many to many relationship Tables.jpg
 
Au niveau objet, pas besoin d'entité intermédiaire. Il est en effet tout à fait possible d'ajouter une propriété de type IEnumerable<CustomerDemographic> (ou une classe l'implémentant) à l'entité Customer et d'ajouter de la même manière une propriété de type IEnumerable<Customer> à l'entité CustomerDemographic. C'est exactement ce que va faire le wizard du designer d'EDM :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/many to many relationship Entities.jpg
 
Les propriétés Customers et CustomerDemographics sont en réalité des System.Data.Objects.DataClasses.EntityCollection<T> (qui implémente IEnumerable<T>).
Afin d'assurer la correspondance entre la table CustomerCustomerDemo et la relation n->n CustomerCustomerDemo, celle-ci est mappée sur la table CustomerCustomerDemo.
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/many to many relationship Mapping.jpg
 
Dans le cas d'une table intermédiaire qui possèderait plus de colonnes que les clés étrangères des deux tables, elles existent par défaut dans le modèle. Cependant, si ces colonnes supplémentaires ne sont pas obligatoires (ie : autorisent la valeur NULL), l'entité intermédiaire pourra être supprimée au profit d'une relation n->n dans le modèle conceptuel (csdl).
« L'entity splitting » est une technique qui consiste à regrouper des informations de plusieurs tables en une seule entité. Par exemple, dans le cas suivant :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/entity splitting Tables.jpg
 
La relation entre Members et Customers a la cardinalité 1->0..1.Dans le cas d'une application qui ne ferait que des statistiques géographiques sur les clients ayant une carte de membre, il serait intéressant de regrouper les informations de la table Members et les informations de la table Customers en une seule entité.Cela est extrêmement simple avec EDM et son designer, il suffit de mapper l'entité Member sur les deux tables :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/entity splitting Entities.jpg
 
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/entity splitting Mapping.jpg
 
Le Mapping permet de rajouter des conditions.
Dans le cas où les statistiques de l'application précédente ne concernent que les clients Londoniens, il serait pénible de systématiquement filtrer les données. L'idée est donc d'appliquer le filtre directement au niveau du modèle.
Dans le cas présent, il faudra appliquer un filtre « City = London ». Pour cela il faut supprimer la propriété City de l'entité Member (renommée en LondonMember) et rajouter la condition « City = London » :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/entity splitting entities with conditions.jpg
 
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/entity splitting Mapping with conditions.jpg
 
La raison pour laquelle il faut supprimer la propriété City de LondonMember est très simple : la classe LondonMember possède la contrainte City = London. Par conséquent, il ne faut pas pouvoir y déroger et la seule façon d'en être sûr c'est de ne pas permettre d'y accéder.
Avec City, il est préférable de retirer également Region et Country et de rajouter la condition « Country = UK ». Le fait de mettre cette dernière condition est très intéressante. Non pas pour éviter le risque d'une ville appelée London en dehors du Royaume-Uni mais pour l'insertion. En effet, lors de l'ajout d'un LondonMember, l'insert réalisé en base remplira également la colonne City et Country avec les valeurs utilisées par les conditions. (Dans le cas du Royaume-Uni, Region = NULL donc inutile de lui mettre une condition).
Dans bien d'autres cas, la notion d'héritage entre Member et Customer est très utile. Bien entendu, EDM répond également à ce besoin.
Pour cela, il suffit de supprimer la relation 1..1 entre Member et Customer, puis de supprimer également la propriété CustomerID de l'entité Member. Ensuite, il faut remplacer la relation 1..1 par une relation d'héritage et enfin mapper la colonne CustomerID sur la propriété CustomerID héritée de l'entité Customer :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPT Entities.jpg
 
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPT Mapping.jpg
 
Comme dit plus haut, l'ObjectContext référence les entités héritant directement de EntityObject. Dans le cas présent, cela signifie que l'ObjectContext aura une propriété Customers de type ObjectQuery<Customer> mais pas de propriétés Members. En effet, Member hérite de Customer. Ce qui est particulièrement intéressant, c'est le fait que la propriété Customers de l'ObjectContext va retourner des instances de Customer mais aussi des instances de Member avec par conséquent, toutes les possibilités induites par le polymorphisme notamment. Dans le cas où le développeur souhaiterait avoir une propriété Members sur son ObjectContext, il peut la rajouter en étendant sa classe partielle :

partial class NorthwindEFEntities

{

    public IQueryable<Member> Members

    {

        get { return Customers.OfType<Member>(); }

    }

}

L'idée ici est diviser une même table en plusieurs classes. Soit la table Employees :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH Tables.jpg
 
Sur cette table, une colonne OutDate indique indirectement si l'employé est toujours en activité ou non. S'il ne l'est plus, la colonne OutType en donnera la raison.
Il est très facilement envisageable de vouloir distinguer les employés encore en activité des autres.
Avec EDM, il est possible d'ajouter une condition sur la nullité d'une colonne. De ce fait, on pourra facilement générer ce modèle :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH Entities.jpg
 
Avec Employee définit comme abstract, EmployeInActivity mappé comme ceci :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH EmployeeInActivity Mapping.jpg
 
Et OutEmployee comme ceci :
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH OutEmployee Mapping.jpg
 
Vu que EmployeeInActivity et OutEmployee englobent l'ensemble des cas (null et non null), Employee doit forcément être abstraite. De plus, dans le cas d'une condition non null, la colonne devra obligatoirement être mappée et la propriété ne devra pas autoriser la valeur null.
Il est possible d'affiner encore la hiérarchie de classe en rajoutant par exemple une classe FiredEmployee qui héritera de OutEmployee avec la condition « OutType = F ».
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH Entities v2.jpg
 
 
/content/5bdd0f35-3fca-491f-b418-986929ca4dcd/TPH FiredEmployee Mapping.jpg
 
Ainsi, une même table peut être associée à n entités.
Tout comme pour le TPT, la propriété Employees de l'ObjectContext retournera des instances d'EmployeeInActivity, de OutEmployee et de FiredEmployee.
Dans la base Northwind, les clients ont une adresse, les employés ont une adresse, les commandes ont une adresse de livraison et les fournisseurs ont une adresse. Pour rajouter un contrôle de cohérence entre le code postal et le pays par exemple, il est évident, qu'il faut une classe Address, encapsulée par les classes Customer, Employee, Order et Supplier. C'est justement le rôle des types complexes. Les complex types n'étant pas visibles dans la version 1 du designer d'EDM, il faudra modifier le XML de l'edmx à la main.
Dans le csdl, il est possible de définir ses propres classes :

<ComplexType Name="Address">

  <Property Name="Adress" Type="String" MaxLength="60" Nullable="true" />

  <Property Name="City" Type="String" MaxLength="15" Nullable="true" />

  <Property Name="Region" Type="String" MaxLength="15" Nullable="true" />

  <Property Name="PostalCode" Type="String" MaxLength="10" Nullable="true" />

  <Property Name="Country" Type="String" MaxLength="15" Nullable="true" />

</ComplexType>

Ensuite, il est possible de dire à une propriété qu'elle est de ce type :

<EntityType Name="Customer">

  <Key>

    <PropertyRef Name="CustomerID" />

  </Key>

  <Property Name="CustomerID" Type="String" Nullable="false" MaxLength="5" FixedLength="true" />

  <Property Name="CompanyName" Type="String" Nullable="false" MaxLength="40" />

  <Property Name="ContactName" Type="String" MaxLength="30" />

  <Property Name="ContactTitle" Type="String" MaxLength="30" />

  <Property Name="Address" Type="Self.Address" Nullable="false" />

  <Property Name="Phone" Type="String" MaxLength="24" />

  <Property Name="Fax" Type="String" MaxLength="24" />

  <NavigationProperty Name="Orders" Relationship="NorthwindEFModel.FK_Orders_Customers" 

                      FromRole="Customers" ToRole="Orders" />

  <NavigationProperty Name="CustomerDemographics" Relationship="NorthwindEFModel.CustomerCustomerDemo" 

                      FromRole="Customers" ToRole="CustomerDemographics" />

</EntityType>

Il faut ensuite définir le mapping :

<EntitySetMapping Name="Customers">

  <EntityTypeMapping TypeName="IsTypeOf(NorthwindEFModel.Customer)">

    <MappingFragment StoreEntitySet="Customers">

      <ScalarProperty Name="CustomerID" ColumnName="CustomerID" />

      <ScalarProperty Name="CompanyName" ColumnName="CompanyName" />

      <ScalarProperty Name="ContactName" ColumnName="ContactName" />

      <ScalarProperty Name="ContactTitle" ColumnName="ContactTitle" />

      <ComplexProperty Name="Address" TypeName="NorthwindEFModel.Address">

        <ScalarProperty Name="Adress" ColumnName="Address" />

        <ScalarProperty Name="City" ColumnName="City" />

        <ScalarProperty Name="Region" ColumnName="Region" />

        <ScalarProperty Name="PostalCode" ColumnName="PostalCode" />

        <ScalarProperty Name="Country" ColumnName="Country" />

      </ComplexProperty>

      <ScalarProperty Name="Phone" ColumnName="Phone" />

      <ScalarProperty Name="Fax" ColumnName="Fax" />

    </MappingFragment>

  </EntityTypeMapping>

</EntitySetMapping>

Grâce à cela et grâce aux classes partielles et aux méthodes partielles, il va être extrêmement simple d'effectuer le contrôle de cohérence :

partial class Address

{

    partial void OnPostalCodeChanging(string value)

    {

        Validate(value, Country);

    }

    partial void OnCountryChanging(string value)

    {

        Validate(PostalCode, value);

    }

    private void Validate(string postal, string country)

    {

        if (string.IsNullOrEmpty(postal) || string.IsNullOrEmpty(country))

            return;

        switch (country.ToUpper())

        {

            case "FRANCE":

                if (!Regex.IsMatch(postal, PostalPatterns.FrancePattern))

                    throw new PostalCountryException { PostalCode = postal, Country = country };

                break;

        }

    }

 

    public class PostalCountryException : InvalidOperationException

    {

        public string PostalCode { get; set; }

        public string Country { get; set; }

    }

}

 Commentaires (19) - Les bases d'Entity Data Model 

Discussion démarée par Dadvdadv le 01/09/2008 à 22:57 , 2 commentaire(s).
Discussion démarée par DadvDadv le 14/08/2008 à 00:38 , 15 commentaire(s).
Discussion démarée par Frédéric Decréquy le 05/04/2008 à 12:00 , 2 commentaire(s).

 Dernières Publications      

Utilisation de jQuery avec ASP.NET MVC
  Développer une IHM à page unique avec ASP.NET MVC et jQuery
par Nicolas Moyère posté le 30/06/2008 à 10:28, lu 824 fois, #0
Tags: ASP.NET MVC, Ajax
Windows Media Center et WCF : développez votre maison intelligente
  Le développement d'applications pour Windows Media Center est facilité avec l'arrivée du SDK 5.3. Même si l'on sent un modèle objet bien lourd derrière, il devient plus facile d'exposer les fonctionnalités de WMC sous la forme de services WCF.
par Frédéric Colin posté le 23/06/2008 à 08:04, lu 891 fois, #0
Notions avancées avec Biztalk Server 2006 R2
  Utilisation des notions d'interchange, corrélation et convoi avec BizTalk Server 2006 R2
par Kader Yildirim posté le 09/06/2008 à 08:04, lu 705 fois, #0
Lucene Persistence Engine pour Evaluant Universal Storage Services
  Suite à l'article de Laurent Kempé, voici un moteur de stockage pour EUSS permettant l'indexation d'entités métier avec Lucene.
par Nicolas Penin posté le 01/06/2008 à 23:38, lu 1091 fois, #1
Tags: C#, Linq
XMLA Trivia : Découverte du XMLA
  Le XMLA (XML for Analysis) est un langage normalisé par plusieurs éditeurs BI pour simplifier l'accès aux données aux cubes et aux métadonnées des bases multidimensionnelles.
par Renaud Harduin posté le 25/05/2008 à 11:57, lu 1008 fois, #1
Exploiter les données CSV via Linq en toute simplicité
  A partir du requêteur dynamique fourni en exemple avec Visual Studio 2008, nous allons essayer de remplir les propriétés d'un ensemble d'objets à partir des données d'un fichier CSV. Nous enrichirons aussi le parseur de nos propres fonctions.
par Frédéric Mélantois posté le 17/05/2008 à 11:41, lu 2785 fois, #0
Comment manipuler simplement le contenu d'un fichier WordML ?
  Manipulations autour du format WordML
par Fabien Reinle posté le 14/05/2008 à 23:55, lu 1405 fois, #0
Polymorphisme et contrats de données WCF
  WCF aborde les types polymorphes du point de vue de la sérialisation. En effet, la connaissance du type réel potentiel est rendue nécessaire dès la description du contrat de données. Une fois n'est pas coutume, j'ai réalisé l'exemple en VB.NET.
par Frédéric Colin posté le 14/05/2008 à 08:40, lu 2931 fois, #2

 Dernières Actualités      

Reprise du projet Reflector par RedGate
  La nouvelle était connue depuis quelques jours par les développeurs de plugins, mais c’est désormais officiel : Lutz Roeder, le responsable de Reflector confie à la société RedGate le futur du projet....
Microsoft publie Visual Studio 2008 Service Pack 1
  Il est recommandé d’utiliser l’outil Visual Studio 2008 Service Pack preparation Tool avant de faire l’installation du Service Pack si vous avez installé des versions béta sur votre machine. Une fois que...
Tags: Framework .NET, Visual Studio 2008
Evaluant dévoile ses sources
  L'ensemble des projets R&D réalisés par les consultants de la SSII Evaluant sont en cours de publication sur CodePlex . L'objectif est de les centraliser et surtout d'augmenter leur visibilité. L'avantage...
Le Silverlight Tour en français!
  Le Silverlight Tour passe maintenant dans les pays francophones! En effet RunAtServer Consulting est partenaire du Silverlight Tour pour la gestion de cette formation Silverlight en français à commencer...
Microsoft publie ASP.NET AJAX 4.0 CodePlex Preview 1
  Cette pré-version contient les améliorations suivantes: Client-side template rendering Declarative instantiation of behaviors and controls DataView control Markup extensions Bindings Vous pouvez en lire...
Tags: Ajax
Deep Earth – Une belle utilisation de Virtual Earth et de Silverlight Deep Zoom
  Ce projet très intéressant est disponible sur Codeplex et vous pouvez voir une démo sur la page suivante . Bien entendu comme touts les projets sur Codeplex vous avez accès aux sources....
Tags: Silverlight