Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Matthieu MEZIL

I love .Net

Abonnements

Actualités

Locations of visitors to this page English blog
Locations of visitors to this blog
EF et le testeur fou

Je me suis amusé à faire des tests idiots avec l'Entity Framework.

A partir de la table Product de Northwind, j'ai réalisé l'héritage suivant :

<EntityType Name="Product">

    <Key>

        <PropertyRef Name="ProductID" />

    </Key>

    <Property Name="ProductID" Type="Int32" Nullable="false" />

    <Property Name="ProductName" Type="String" Nullable="false" MaxLength="40" Unicode="true" FixedLength="false" />

    <Property Name="SupplierID" Type="Int32" />

    <Property Name="CategoryID" Type="Int32" />

    <Property Name="QuantityPerUnit" Type="String" MaxLength="20" Unicode="true" FixedLength="false" />

    <Property Name="UnitPrice" Type="Decimal" Precision="19" Scale="4" />

    <Property Name="UnitsInStock" Type="Int16" />

    <Property Name="UnitsOnOrder" Type="Int16" />

</EntityType>

<EntityType Name="ProductReorder0" BaseType="NorthwindModel3.Product" >

    <Property Name="Discontinued" Type="Boolean" Nullable="false" />

</EntityType>

<EntityType Name="DiscontinuedProduct" BaseType="NorthwindModel3.Product" >

    <Property Name="ReorderLevel" Type="Int16" Nullable="true" />

</EntityType>

avec le mapping suivant :

<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel3.ProductReorder0)">

    <MappingFragment StoreEntitySet="Products" >

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

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

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

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

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

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

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

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

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

        <Condition ColumnName="ReorderLevel" Value="0" />

    </MappingFragment>

</EntityTypeMapping>

<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel3.DiscontinuedProduct)">

    <MappingFragment StoreEntitySet="Products" >

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

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

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

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

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

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

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

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

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

        <Condition ColumnName="Discontinued" Value="true" />

    </MappingFragment>

</EntityTypeMapping>

<EntityTypeMapping TypeName="NorthwindModel3.Product">

    <MappingFragment StoreEntitySet="Products">

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

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

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

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

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

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

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

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

        <Condition ColumnName="Discontinued" Value="false" />

    </MappingFragment>

</EntityTypeMapping>

Concrètement, le test que je voulais faire est : que se passe-t-il si l'ObjectContext ne sait pas qu'elle type il doit instancier ? En effet, dans le cas de Northwind, j'ai des produits avec ReoderLevel = 0 et Discontinued = true. Donc que doit-il instancier ? un ProductReorder0 ou un DiscontinuedProduct ?

Bien entendu, j'ai une erreur sur mon edm, cependant, le compilateur n'utilisant que le code généré par mon edm, l'erreur n'est pas blocante.

Voici les tests que j'ai effectué :

using (var context = new NorthwindEntities())

{

    var q = context.Products.OfType<DiscontinuedProduct>();

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q = context.Products.OfType<ProductReorder0>();

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q1 = context.Products.OfType<DiscontinuedProduct>();

    foreach (var p in q1)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

    var q2 = context.Products.OfType<ProductReorder0>();

    foreach (var p in q2)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

using (var context = new NorthwindEntities())

{

    var q = context.Products;

    foreach (var p in q)

        if (p.GetType() != typeof(Product))

            Console.WriteLine(p.GetType());

}

Dane les deux premiers cas, cela se passe très bien, ce qui est très intéressant. Cela signifie en effet que le OfType n'influe pas que sur la requête sql généré mais aussi sur l'instanciation des entités.

Dans le troisième cas, lorque l'on va exécuter q2, on va avoir une exception. En effet, les 3 types sont liés au même EntitySet. Chaque entité a une EntityKey qui doit être unique pour l'EntitySet. Dans le troisième cas, le premier Produit avec Discontinued = true et ReorderLevel = 0 génèrera donc une exception.

Dans le quatrième cas, j'ai également une exception car il ne sait pas quoi instancier.

Posté le vendredi 18 juillet 2008 07:34 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , , ,

Dans quelle salle le C# a-t-il été inventé ?
Si vous voulez connaître la réponse ou, plus intéressant, entendre la Design Team de C#4, vous devriez regarder cette video channel9

Posté le mercredi 16 juillet 2008 22:04 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : ,

Bien débuter avec l'entity framework

Depuis plusieurs mois, je vous parle de l'entity framework. Le problème quand on commence à bien maîtriser une techno c'est qu'on a tendance à parler de concepts comme s'ils étaient acquis pour tout le monde ce qui n'est pas toujours le cas.

Ce post a donc pour but de récapituler un certain nombre de notions basiques.

L'Entity Framework est un outil de Mapping Objet Relationnel développé par Microsoft qui paraîtra en version 1 avec le SP1 de VS 2008.

Le mapping proposé par l'Entity Framework n'est pas un mapping 1->1 comme avec LINQ To SQL mais un mapping beaucoup plus puissant. AFin de définir ce mapping, l'Entity Framework introduit la notion d'Entity Data Model (EDM).

L'EDM se décompose en 3 parties :

  • la description de la base de données (SSDL)
  • la description des entités (CSDL)
  • la description du mapping entre les deux (MSL)

Quand on veut utiliser l'entity framework, on rajoute un item de type ADO.NET Entity Data Model qui va générer un fichier edmx à notre solution avec un fichier .designer.cs / vb lié.

Ce fichier edmx contient les 3 parties d'EDM plus une 4ème partie nettement moins intéressante : la position des entités et des relations dans le designer.

Lors de la compilation chacune des 3 parties est éclatée en un fichier situé dans le répertoire bin\Debug / Release.

L'entity framework supporte n'importe quel système de persistance à condition d'avoir le provider. Par défaut MS fournit ceux pour SQL Server. Pour plus d'infos sur les providers disponibles, consultez le post de l'ADO .Net Team.

La chaîne de connection est différente des chaînes de connections telle qu'on les connaissait avec les versions précédentes. En effet, elles incluent en plus, le chemin du ssld, du csdl, du msl et le provider à utiliser.

Au niveau du mapping, il y a plusieurs manipulations possibles. Pour plus d'infos à ce sujet, je vous invite à consulter mon article.

L'Entity Framework supporte LINQ avec LINQ To Entities. Les requêtes LINQ sont traduites en ESQL. ESQL est un nouveau langage introduit par l'Entity Framework. Chaque provider doit être capable de traiter le ESQL. Il est possible d'écrire les requêtes directement en ESQL à la place de LINQ To Entities mais cela revient à perdre les avantages de LINQ : vérification à la compilation, intellisence. Cependant dans certains cas, cela peut s'avérer très pratique.

Avec l'Entity Framework, tout se fait à partir d'un ObjectContext. L'ObjectContext va nous permettre d'exécuter nos requêtes, de tracker les changements et de faire la persistance en base.

Daniel Simmons a enregistré une émission très basique sur l'Entity Framework pour dnrTV que je vous conseille bien évidemment de visionner.

Pour plus d'info, je vous invite à consulter la FAQ.

Si vous avez des questions, le forum msdn est très réactif. Vous pouvez aussi me poser vos questions en utilisant les commentaires.

Posté le mercredi 16 juillet 2008 19:30 par Matthieu MEZIL | 4 commentaire(s)

Classé sous : , , ,

Kirill Osenkov, 10 contre 1 que vous allez être surpris

Je ne sais même pas comment résumer ce post sans recopier tout le code tellement c'est surprenant.

Le mieux c'est d'aller voir le post de Kirill Osenkov directement.

Posté le mardi 15 juillet 2008 07:51 par Matthieu MEZIL | 1 commentaire(s)

Classé sous : ,

EF : Comment cloner les entités ?

Dans mon dernier post sur l'Entity Framework, je vous ai dis que la pattern classique de détachement / attachement incluait un Clone de l'entité.

Cependant, la question que certains d'entre-vous se sont posée est : comment cloner une entité ?

Il y a au moins deux façons de faire :

  • Par reflection (avec possibilité d'optimisation) tel que je l'ai fait ici.
  • Utiliser la sérialisation / désérialisation. Pour cela je vous renvoie sur la réponse de Patrick Magee.

Posté le mardi 15 juillet 2008 07:32 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , ,

Ecriture d'un provider IQueryable
La semaine dernière, je vous disais que Matt Warren avait repris sa série sur l'écriture d'un provider IQueryable. Il vient de publier le 11ème post de sa série.

Posté le mardi 15 juillet 2008 06:40 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : ,

La mise à jour en mode optimiste

Le pattern classique de détachement / attachement des entités est le suivant :

Le serveur récupère l'entité.
Le serveur l'envoit au client qui va la cloner.
Pour faire un update, le client va renvoyer au serveur l'entité initiale + l'entité modifiée.
Ensuite, le serveur instancie un nouveau contexte, attache l'entité initiale, appelle la méthode ApplyPropertyChanges avec l'entité modifié et enfin sauvegarde le contexte.

Cependant dans le cas où l'on ne se soucie pas des modifications éventuellement faites entre temps et qu'on considère que le dernier qui écrit a raison pour l'ensemble des propriétés, on peut se passer de l'entité initiale.
Avant de faire le ApplyPropertyChanges, il faut s'assurer que l'entité est bien dans le contexte. Pour cela, il suffit de la récupérer, ce que l'on va faire avec la méthode TryGetObjectByKey. Ensuite, l'appel de ApplyPropertyChanges et SaveChanges suffira à mettre à jour la base.

Cet exemple de code n'est pas un code client / serveur mais permet de valider la solution que je propose.

NorthwindModel.Categories c;

NorthwindModel.Categories c2;

using (var context = new NorthwindModel.NorthwindEntities())

{

    context.Categories.MergeOption = System.Data.Objects.MergeOption.NoTracking;

    c = context.Categories.First();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    c2 = context.Categories.First();

    context.DeleteObject(c2);

    context.SaveChanges();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    object o;

    context.TryGetObjectByKey(c.EntityKey, out o);

    context.ApplyPropertyChanges("Categories", c);

    context.SaveChanges();

}

Avec l'attachement puis le ApplyPropertyChanged suivi du SaveChanges ne fait rien si le row a été entre temps supprimé en base.

Avec le code précédent, on aura une exception sur le ApplyPropertyChanges car le TryGetObjectByKey ne chargera rien dans le contexte. Par contre avec le code suivant, non seulement on n'aura plus d'exception mais en plus, le SaveChanges recréera le row en base si celui-ci a été supprimé entre temps.

NorthwindModel.Categories c;

NorthwindModel.Categories c2;

using (var context = new NorthwindModel.NorthwindEntities())

{

    context.Categories.MergeOption = System.Data.Objects.MergeOption.NoTracking;

    c = context.Categories.First();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    c2 = context.Categories.First();

    context.DeleteObject(c2);

    context.SaveChanges();

}

using (var context = new NorthwindModel.NorthwindEntities())

{

    object o;

    context.TryGetObjectByKey(c.EntityKey, out o);

    if (o == null)

        context.AddToCategories(c);

    else

        context.ApplyPropertyChanges("Categories", c);

    context.SaveChanges();

}

Posté le vendredi 11 juillet 2008 21:54 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : ,

Ecriture d'un provider IQueryable

6 mois après son dernier posts sur la création d'un provider IQueryable, Matt Warren vient de publier le 10ème post de sa série.

http://blogs.msdn.com/mattwar/archive/2008/07/08/linq-building-an-iqueryable-provider-part-x.aspx

Posté le mercredi 9 juillet 2008 20:27 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : ,

Une table -> deux entity types sans colonne discriminante en base, gestion des relations

Avec mes deux précédents posts (ssdl view et TPT et ssdl view et TPH), je vous ai montré comment passer d'une table Employees à deux entity types : Employee et Manager avec Manager qui hérite de Employee.

Maintenant, allons un peu plus loin. Dans ma table, j'ai une FK entre Employees.EmployeeManager et Employees.EmployeeId.

Dans mon modèle, je me retrouve donc avec une relation entre Employee et Employee comme en base.

Le problème c'est que ce que je voudrais, c'est une relation entre Manager et Employee.

Pour cela, il faut que je commence par supprimer la relation du CSDL puis la relation du SSDL (possible uniquement en modifiant le xml de l'edmx à la main). Dans le cas contraire, j'aurai une erreur dans mon EDM. Dans l'hypothèse où on supprime la relation CSDL directement dans le xml, il faut alors la supprimer également dans la partie MSL. Avec le designer, cela est fait automatiquement. 

Ensuite, je rajoute une relation entre Manager et Employee que je mappe sur la table Employees.

Cool ça marche !

Maintenant, allons plus loin, comment faire passer un employé au poste de manager ? Autrement dit, comment rajouter des sous-employés à un employé ?

Voici un petit bout de code qui permet de faire ça :

var manager = new Manager { EmployeeId = oldEmployee.EmployeeId, EmployeeManager = oldEmployee.EmployeeManager, EmployeeName = oldEmployee.EmployeeName };

ObjectStateEntry ose;

context.ObjectStateManager.TryGetObjectStateEntry(manager, out ose);

if (ose == null)

    context.AddToEmployeesWithManager(manager);

context.Detach(oldEmployee);

context.AcceptAllChanges();

manager.SubEmployees.Add(new Employee { EmployeeId = newEmployeeId, EmployeeName = newEmployeeName });

context.SaveChanges();

Et voilà, le tour est joué !

Maintenant apportons quelques explications à ce code.

Quand on affecte l'EmployeeManager à manager, cela a pour effet de rajouter l'entité manager au contexte de oldEmployee.

Cependant, dans le cas où le contexte de oldEmployee ne serait pas notre instance context ou dans le case où EmployeeManager serait null, il va falloir ajouter notre nouveau manager à notre instance context.

D'où le test avec TryGetObjectStateEntry.

Une fois cela fait, il faut détacher oldEmployee afin de ne pas avoir de conflit.

Maintenant qu'on a une instance de manager, on va pouvoir rajouter des sous-employés. Cependant, côté base, nous n'avons rien changé (le record sera le même) mais si on fait un SaveChanges, on va avoir un problème car l'instance de manager est marquée comme Added. Il faut donc lui dire que rien n'a été modifié, d'où le AcceptAllChanges.

Ensuite, on rajoute notre(nos) nouveau(x) employé(s). Il ne reste alors plus qu'à persister le contexte en base.

Posté le dimanche 6 juillet 2008 13:53 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , ,

ssdl view et TPT

Reprenons l'exemple précédent et faisons la même chose avec du TPT.

Au niveau du ssdl, il suffit de rajouter ceci :

<EntitySet Name="Managers" EntityType="TestModel1.Store.Managers">

    <DefiningQuery>

        SELECT DISTINCT EmployeeManager

        FROM EmployeesWithManager

    </DefiningQuery>

</EntitySet>

et

<EntityType Name="Managers">

    <Key>

        <PropertyRef Name="EmployeeManager" />

    </Key>

    <Property Name="EmployeeManager" Type="int" Nullable="false" />

</EntityType>

Ensuite, il suffit de créer l'entity type Manager, le faire hériter de Employee, le mapper sur notre vue ssdl en lui disant que la colonne EmployeeManager est mappée sur EmployeeId.

C'est vraiment trop simple l'Entity Framework Wink

Posté le samedi 5 juillet 2008 02:04 par Matthieu MEZIL | 2 commentaire(s)

Classé sous : , ,

ssdl view et TPH

Ce post s'inspire d'un post précédent "EF : Comment faire de l'Entity Splitting avec des PK différentes ?"

L'idée ici est la suivante. On a une table Employee avec 3 colonnes : EmployeeId (PK), EmployeeName et EmployeeManager (FK vers EmployeeId).

De là, je voudrais générer avec EDM deux entity types : Employee et Manager avec Manager héritant d'Employee.

Comment faire cela ?

L'idée est d'utiliser une ssdl view pour faire du TPH.

Il suffit de rajouter le code suivant dans le ssdl :

<EntitySet Name="EmployeeWithManagerStatus" EntityType="TestModel.Store.EmployeeWithManagerStatus">

    <DefiningQuery>

        SELECT EWM2.EmployeeId, CAST(CASE(EWM2.SubEmployeesCount) WHEN 0 THEN 0 ELSE 1 END AS BIT) AS IsManager

        FROM

        (SELECT EmployeeId, (

        SELECT count(1)

        FROM EmployeesWithManager

        WHERE EmployeeManager = EWM1.EmployeeId) AS SubEmployeesCount

        FROM EmployeesWithManager AS EWM1) AS EWM2

    </DefiningQuery>

</EntitySet>

<EntityType Name="EmployeeWithManagerStatus">

    <Key>

        <PropertyRef Name="EmployeeId" />

    </Key>

    <Property Name="EmployeeId" Type="int" Nullable="false" />

    <Property Name="IsManager" Type="bit" />

</EntityType>

A ce stade, tout le travail est quasiment fait. Il suffit alors de rajouter une entité Manager, de rajouter l'héritage entre Manager et Employee puis de mapper l'entity type Manager sur notre vue ssdl. En dernier lieu, il suffit de rajouter une condition sur le mapping en précisant que pour les Managers, la colonne IsManager = true.

Et voilà, le tour est joué. Sympa non ?

Posté le vendredi 4 juillet 2008 19:12 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , ,

Webcasts sur le Parallel Framework disponibles
Pour ceux qui comme moi n'ont pas eu la chance d'assister au mercredi du dev sur le parallel framework, mais aussi pour les autres, les webcasts sont en ligne

Posté le vendredi 4 juillet 2008 17:26 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : ,

EF : Comment faire de l'Entity Splitting avec des PK différentes ?

Imaginons que l'on veuille écrire une application dont le but est de gérer les stocks de produits en se basant sur Northwind sans avoir à les créer.

Dans cette optique, on souhaite avoir deux Entitysets : Product et Supplier avec Supplier en ReadOnly.

On aimerait également avoir une propriété CategoryName ReadOnly dans Product.

 

Comment faire cela avec un minimum de code ?

Pour commencer, il faut passer tous les set de Supplier à private. Pour cela, on modifiera l'attribut xml Setter.

Bien entendu, l'affectation du Setter à Private peut se faire via la fenêtre de properiétés du designer d'EDM (depuis VS 2008 SP1 Beta).

Ensuite, on va passer le set de la propriété Supplier de Product à private et passer la propriété (get + set) Products de Supplier à private.

On va également supprimer la propriété CategoryID de Product.

A ce niveau là, votre csdl doit ressembler à ça :

<!-- CSDL content -->

<edmx:ConceptualModels>

    <Schema Namespace="NorthwindEFModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">

        <EntityContainer Name="NorthwindEFEntities">

            <EntitySet Name="Products" EntityType="NorthwindEFModel.Product" />

            <EntitySet Name="Suppliers" EntityType="NorthwindEFModel.Supplier" />

            <AssociationSet Name="FK_Products_Suppliers" Association="NorthwindEFModel.FK_Products_Suppliers">

                <End Role="Suppliers" EntitySet="Suppliers" />

                <End Role="Products" EntitySet="Products" />

            </AssociationSet>

        </EntityContainer>

        <EntityType Name="Product">

            <Key>

                <PropertyRef Name="ProductID" />

            </Key>

            <Property Name="ProductID" Type="Int32" Nullable="false" />

            <Property Name="ProductName" Type="String" Nullable="false" MaxLength="40" Unicode="true" FixedLength="false" />

            <Property Name="QuantityPerUnit" Type="String" MaxLength="20" Unicode="true" FixedLength="false" />

            <Property Name="UnitPrice" Type="Decimal" Precision="19" Scale="4" />

            <Property Name="UnitsInStock" Type="Int16" />

            <Property Name="UnitsOnOrder" Type="Int16" />

            <Property Name="ReorderLevel" Type="Int16" />

            <Property Name="Discontinued" Type="Boolean" Nullable="false" />

            <NavigationProperty Name="Supplier" Relationship="NorthwindEFModel.FK_Products_Suppliers" FromRole="Products" ToRole="Suppliers" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

        </EntityType>

        <EntityType Name="Supplier">

            <Key>

                <PropertyRef Name="SupplierID" />

            </Key>

            <Property Name="SupplierID" Type="Int32" Nullable="false" />

            <Property Name="CompanyName" Type="String" Nullable="false" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="ContactName" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="ContactTitle" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="Address" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="City" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="Region" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="PostalCode" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="Country" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="Phone" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="Fax" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <Property Name="HomePage" Type="String" Nullable="true" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            <NavigationProperty Name="Products" Relationship="NorthwindEFModel.FK_Products_Suppliers" FromRole="Suppliers" ToRole="Products" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" a:GetterAccess="Private" />

        </EntityType>

        <Association Name="FK_Products_Suppliers">

            <End Type="NorthwindEFModel.Supplier" Role="Suppliers" Multiplicity="0..1" />

            <End Type="NorthwindEFModel.Product" Role="Products" Multiplicity="*" />

        </Association>

    </Schema>

</edmx:ConceptualModels>

Il nous reste maintenant à rajouter notre propriété CategoryName sur Product.

Comment faire cela. On peut bien sûr utiliser une vue à la place de la table Products et des procédures stockées ou même définir une vue ssdl avec des fonctions ssdl pour les opérations CUD (pour plus d'infos, voir mon article sur EDM).  Cependant, nous sommes partisant du moindre effort ("Comment faire cela avec un minimum de code ?"). Pour cela, nous allons définir une vue ssdl réduite et nous allons utiliser l'EntitySplitting pour garder la partie de Product déjà générée par le designer. De plus cette approche nous évitera de coder les functions ssdl vu que l'on suppose que la propriété CategoryName de Product est en ReadOnly.

Pour cela, dans le SSDL, on va définir un nouveau EntitySet :

<EntitySet Name="ProductCategoryName" EntityType="NorthwindEFModel.Store.ProductCategoryName">

    <DefiningQuery>

        SELECT P.ProductID, C.CategoryName

        FROM Products AS P

        INNER JOIN Categories as C ON C.CategoryID = P.CategoryID

    </DefiningQuery>

</EntitySet>

et un nouveau EntityType (toujours dans le SSDL) :

<EntityType Name="ProductCategoryName">

    <Key>

        <PropertyRef Name="ProductID" />

    </Key>

    <Property Name="ProductID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />

    <Property Name="CategoryName" Type="nvarchar" MaxLength="15" />

</EntityType>

Maintenant, on peut revenir en mode design et ajouter la propriété CategoryName (string avec setter à Private) et faire de l'EntitySplitting sur Product.

Et voila, c'est (déjà) fini. Vous avez un EntityType Supplier ReadOnly et un EntityType Product avec une propriété CategoryName ReadOnly.

Posté le mercredi 18 juin 2008 02:17 par Matthieu MEZIL | 0 commentaire(s)