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; }
}
}