J’ai réalisé un système plutôt simple permettant à un service WCF de mettre à disposition des classes côté client sans référencer les DLLs contenant ces classes dans le projet client, ni qu’elles soient forcément utilisés dans le code du service, et ce, sans recompiler votre service WCF à chaque fois que vous voulez rendre disponible ou non telle ou telle DLL du service.
A quoi un truc pareil pourrait bien servir… alors plus concrètement, voici un exemple :
Vous créez une méthode dans votre service WCF avec un paramètre d’entrée de type ObjetMetier mais vous souhaitez que le client appelle cette méthode avec un objet de type CustomObjetMetier qui dérive de ObjetMetieret, qui plus est, ne se trouve pas forcément dans le même assembly que la classe ObjetMetier.
Pour corser le tout, vous ne voulez pas référencer les assemblies contenant vos types ObjetMetier et CustomObjetMetier côté client afin d’assurer une meilleure interopérabilité.
Premier problème.. Votre méthode WCF contenant dans sa signature de méthode un paramètre de type ObjetMetier, le WSDL généré ne vous donnera la signature que de l’objet ObjetMetier. Votre client aura donc comme seule ressource l’objet ObjetMetier.
Deuxième problème…… Même si vous arrivez à passer à la méthode du service un objet CustomObjetMetier, il s’efforcera d’essayer de le ‘déserialiser’ en tant qu’objet de type ObjetMetier, ce qui va vous retourner une jolie petite exception..
Bref, pour les interessés par cette problématique, voilà ma solution ci-dessous :
Pré-requis pour ceux qui ne connaitraient pas bien WCF :
- Les classes que vous voudrez rendre disponible côté client doivent implémenter l’attribut [DataContract].
- Les propriétés de ces classes doivent quant à elles implémenter l’attribut [DataMember].
Tout ce qui suit doit être implémenter au niveau de votre service WCF.
1. Tout d’abord il vous faut ajouter des références dans votre projet vers toutes les dll que vous voudrez rendre disponible côté client. Je suppose que tout le monde sait faire..
Vous allez ensuite créer un appSetting dans le fichier config permettant d’énumérer les assemblies dont vous voulez référencer les classes côté client (séparées par des point-virgules) :
<add key="ObjetMetierAssemblies" value="Service.ObjetMetiers.Base;Service.ObjetMetiers.Custom"/>
2. La majeure partie du travail consiste en l’implémentation de la classe ci-dessous :
static class KnownTypesHelper
{
///
/// Gets the types to be known on the client side.
///
/// The provider.
///
public static IEnumerable GetKnownTypes(ICustomAttributeProvider provider)
{
//Types to be known by the client side
List knownTypes = new List();
//Check if assemblies references are needed on the client side
if (ConfigurationSettings.AppSettings["ObjetMetierAssemblies"] == null
|| ConfigurationSettings.AppSettings["ObjetMetierAssemblies"] == string.Empty)
{
//return empty list
return knownTypes;
}
//Get the list of assembly names for the classes needed on the client side
string[] assemblyNamesList = ConfigurationSettings.AppSettings["ObjetMetierAssemblies"].Split(new char[1] { ';' });
foreach (string assemblyName in assemblyNamesList )
{
if (assemblyName != string.Empty)
{
//Get assembly object from assembly name
Assembly assembly = Assembly.LoadWithPartialName(assemblyName );
if (assembly != null)
{
//Get types contained in assembly
foreach (Type type in assembly.GetTypes())
{
foreach (object customAttribute in type.GetCustomAttributes(true))
{
//If the current type has a DataContract attribute,
//we add the type to the result list
if (customAttribute.GetType() == typeof(DataContractAttribute))
{
knownTypes.Add(type);
}
}}}}}
//Return types to be known on the client side
return knownTypes;
}}
Cette classe va lister toutes les assemblies référencées dans le fichier config (cf 1.) pour en extraire les types qui contiennent l’attribut [DataContract] et retourner la liste de ces derniers.
3. Pour finir vous rajoutez au dessus de l’interface de votre service l’attribut ServiceKnownType comme ci-dessous, avec le nom de la méthode et le type de la classe qui contient la méthode :
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesHelper))]
public interface IService
{
[OperationContract]
ObjetMetier DoSomething(ObjetMetier value);
}
Le tour est joué, en ajoutant une référence au service dans un projet client, vous aurez accès à toutes les classes des assemblies listées dans le fichier de config, pour peu qu’elles soient munies de l’attribut [DataContract] .
Julien Dumas Microsoft reflexion, WCF