[WCF] DataContract et héritage
Encore une fois, j'exhume un petit peu de code pour répondre à un problème de POO qui semble simple, mais qui peut devenir cauchemardesque dès que l'on y associe WCF et les DataContract.
Le scénario est le suivant :
- On a prévu d'utiliser une classe (Item) comme argument d'une méthode de service (Service1).
- Côté client, l'objet réellement manipulé est une instance d'une classe qui hérite d'Item (ItemDerived).
- Côté client, la classe de base n'est jamais manipulée directement.
- Chaque classe ou interface est dans un namespace ou un assembly différent
Le diagramme de classe donne ceci
Pour appeler le service on utilise un code tel que celui-ci :
var result = proxy.Process(new ItemDerived());
Ou celui-là :
var result = proxy.Process((Item)new ItemDerived());
En l'état, les appels au service provoquent systématiquement une exception du style : le type utilisé lors de la sérialisation n'est pas du type attendu
Note : les messages d'exception semblent varier d'une version du Framework à l'autre.
Dans les faits, lors de la sérialisation, le DataContractSerializer est perdu :
- Il attend une classe Item dans un namespace X avec un nom Y avec un DataContract (namespace X, name Y)
- Il reçoit une classe ItemDerived dans un namespace U avec un nom V sans DataContract (on utilise juste l'héritage).
Il faut donc aider notre DataContractSerializer à s'en sortir. La solution est toute simple : partager une information claire et commune, le contrat.
La classe item est donc modifiée pour exposer deux constantes : le DataContractNamespace et le DataContractName.
Si on ne conserve que les déclarations de classes et des constantes, on obtient ceci :
namespace Demos.Wcf.Base
{
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract(Name = DataContractName, Namespace = DataContractNamespace)]
public class Item
{
protected const String DataContractName = "Item";
protected const String DataContractNamespace = "Datas";
}
}
namespace Demos.Wcf.Inherit
{
[DataContract(Name = DataContractName, Namespace = DataContractNamespace)]
public class ItemDerived : Item
{
}
}
En partageant ces informations, on peut maintenant utiliser des classes dérivées avec notre proxy. Il n'y a plus d'exceptions.
Simple comme WCF ;)