EDM : comment contourner les problématiques liées aux bases inadéquates
Durant la formation que j'anime cette semaine, un de mes stagiaires a essayé de me coller en me donnant une base pour laquelle l'utilisation d'EF n'était, soit disant, pas possible. Heureusement, mon honneur et celui de l'EF est sauf... 
En effet, cette base n'est pas idéale pour EF. Elle est composée
- d'une table Bases avec 4 colonnes:
- Id, int, Identity (PK)
- Key, int, not null
- Value, int, not null
- Text, nvarchar
Value est un entier compris entre 1 et 9 qui ne peut plus évoluer une fois qu'il est affecté et Key est égale à Id * 10 + Value
- d'une table Inheriteds avec 3 colonnes:
- Id, int, Indentity (PK)
- Key, int, not null
- Text2, nvarchar
Key est la même que dans la table Bases et donc, on a en réalité une relation 1 -> 0..1 entre Bases et Inheriteds même si cette relation n'existe pas en base.
- d'une table Descriptions avec 4 colonnes:
- Id, int, Identity (PK)
- Key, int, not null
- Language, nchar
- Text, nvarchar
Key est, là aussi, la même que Bases avec une relation (non présente en base) 1 -> * entre Bases and Descriptions, comme si la PK de Descriptions était composée de Key et de Language.
Côté CSDL (description des entités), il voulait avoir :
- une classe Base asbtraite (mappée sur Bases)
- une classe Entity qui hérite de Base (mappée sur Inheriteds)
- une classe Description
- une relation 1 -> * entre Base et Description
Est-il possible de réaliser ce scénario avec EF ? Voud l'aurez compris en lisant le débit de ce post, yes we can! 
Comment faire cela ?
- Tout d'abord, dans le designer, après avoir importé les tables and avoir renommer les entity types générées, il fait dupprimer la propriété Id de chacune d'entre elles.
- Ensuite, il faut définir Base.Key, Description.Key et Description.Language comme des EntityKey.
- Définir Base comme abstraite
- Supprimer Entity.Key
- Ajouter la relation d'héritage entre Entity et Base et mettre à jour le mapping de Entity (ie: il faut mapper la colonne Key sur la propriété Base.Key)
- Ajouter la relation entre Base et Description
Bien entendu, quand on en est là, notre EDM n'est pas correcte (rien ne permet de garantir l'intégrité des données ni l'unicité des entity keys).
Pour résoudre cela, il fait changer le SSDL (description de la BD) et simuler une base "idéale" sans Id avec Key comme PK).
Comme Key est calculée, il faut changer son StoreGeneratedPattern et le passé à Computed.
Ensuite, il faut gérer les opérations CUD de la base sur Base car il faut calculer la KeyValue.
Base étant abstraite donc il faut définir les opérations CUD pour ses entity types héritées (Entity dans notre cas).
<EntityContainer Name="TestModelStoreContainer">
<EntitySet Name="Bases" EntityType="TestModel.Store.Bases" store:Type="Tables" Schema="dbo" />
<EntitySet Name="Descriptions" EntityType="TestModel.Store.Descriptions" store:Type="Tables" Schema="dbo" />
<EntitySet Name="Inheriteds" EntityType="TestModel.Store.Inheriteds" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="Bases">
<Key>
<PropertyRef Name="Key" />
</Key>
<!--<Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />-->
<Property Name="Key" Type="int" Nullable="false" StoreGeneratedPattern="Computed" />
<Property Name="Value" Type="int" Nullable="false" />
<Property Name="Text" Type="nvarchar" MaxLength="50" />
</EntityType>
<EntityType Name="Descriptions">
<Key>
<PropertyRef Name="Key" />
<PropertyRef Name="Language" />
</Key>
<!--<Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />-->
<Property Name="Key" Type="int" Nullable="false" />
<Property Name="Language" Type="nchar" Nullable="false" MaxLength="20" />
<Property Name="Text" Type="nvarchar" MaxLength="50" />
</EntityType>
<EntityType Name="Inheriteds">
<Key>
<PropertyRef Name="Key" />
</Key>
<!--<Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />-->
<Property Name="Key" Type="int" Nullable="false" StoreGeneratedPattern="Computed" />
<Property Name="Text2" Type="nvarchar" MaxLength="50" />
</EntityType>
<Function Name="InsertEntity" IsComposable="false">
<CommandText>
INSERT INTO Bases
([Key], Value ,[Text])
VALUES
(-1, @Value, @Text)
DECLARE @IdValue int
SET @IdValue = SCOPE_IDENTITY()
DECLARE @KeyValue int
SET @KeyValue = @IdValue * 10 + @Value
UPDATE Bases
SET [Key] = @KeyValue
WHERE Id = @IdValue
INSERT INTO Inheriteds
([Key], Text2)
VALUES
(@KeyValue, @Text2)
SELECT @KeyValue AS KeyValue
</CommandText>
<Parameter Name="Value" Type="int" />
<Parameter Name="Text" Type="nvarchar" MaxLength="50" />
<Parameter Name="Text2" Type="nvarchar" MaxLength="50" />
</Function>
<Function Name="UpdateEntity" IsComposable="false">
<CommandText>
UPDATE Bases
SET [Text] = @Text
WHERE [Key] = @Key
UPDATE Inheriteds
SET Text2 = @Text2
WHERE [Key] = @Key
</CommandText>
<Parameter Name="Key" Type="int" />
<Parameter Name="Text" Type="nvarchar" MaxLength="50" />
<Parameter Name="Text2" Type="nvarchar" MaxLength="50" />
</Function>
<Function Name="DeleteEntity" IsComposable="false">
<CommandText>
DELETE Inheriteds
WHERE [Key] = @Key
DELETE Bases
WHERE [Key] = @Key
</CommandText>
<Parameter Name="Key" Type="int" />
</Function>
Maintenant, il ne reste plus qu'à définir le mapping de l'entity type Entity avec mes SSDL functions:
<EntitySetMapping Name="BaseSet">
<EntityTypeMapping TypeName="IsTypeOf(TestModel.Base)">
<MappingFragment StoreEntitySet="Bases">
<ScalarProperty Name="Key" ColumnName="Key" />
<ScalarProperty Name="Value" ColumnName="Value" />
<ScalarProperty Name="Text" ColumnName="Text" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="TestModel.Entity">
<MappingFragment StoreEntitySet="Inheriteds">
<ScalarProperty Name="Key" ColumnName="Key" />
<ScalarProperty Name="Text2" ColumnName="Text2" />
</MappingFragment>
<ModificationFunctionMapping>
<InsertFunction FunctionName="TestModel.Store.InsertEntity">
<ScalarProperty Name="Value" ParameterName="Value"/>
<ScalarProperty Name="Text" ParameterName="Text"/>
<ScalarProperty Name="Text2" ParameterName="Text2"/>
<ResultBinding Name="Key" ColumnName="KeyValue"/>
</InsertFunction>
<UpdateFunction FunctionName="TestModel.Store.UpdateEntity">
<ScalarProperty Name="Key" ParameterName="Key" Version="Current"/>
<ScalarProperty Name="Text" ParameterName="Text" Version="Current"/>
<ScalarProperty Name="Text2" ParameterName="Text2" Version="Current"/>
</UpdateFunction>
<DeleteFunction FunctionName="TestModel.Store.DeleteEntity">
<ScalarProperty Name="Key" ParameterName="Key"/>
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>
Mon modèle est maintenant valide. Je peux avoir une bonne conception de mes entités sans me soucier des contraintes de la base.
Alors, c'est pas trop fort EF ? Moi je dis : franchement, EF, c'est trop la classe ! 
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :