See Sharp
Is it really just a matter of point of view ?

Joey doesn't share food, WCF doesn't share Types (unless you ask them nicely ?)

April 23, 2009 09:37 by salfab

Introduction 

Au menu de ce jour, un peu de WCF. Celà faisait longtemps que nous n'avions plus joué avec ce joli jouet... Il est l'heure de se remettre à l'ouvrage !

Scénario

Soit le scénario suivant :

  • un service expose un type "CompositeType" et une opération retournant une instance de CompositeType
  • un second service ayant une serviceReference sur le premier pour connaitre "CompositeType" (CompositeType est alors un ProxyObject dans le second service)
    et exposant une méthode acceptant un paramètre de type "CompositeType" (Proxy object)
  • Un client consommant les deux services, utilisant le premier pour récupérer une instance de CompositeType et invoquant une opération du second acceptant unb CompositeType comme paramètre

Pour mettre les idées au clair, voici un petit exemple de notre situation
WCFTypeSharing.zip (108,57 kb)

CompositeType provient dans tous les cas du DataContract défini dans le premier service, mais comme le second service consomme le premier et expose son propre proxy object, les namespaces ne sont pas les mêmes dans les deux cas : même s'ils seront sérialisés de la même manière, la CLR les considèrera comme deux objets différents.

Résoudre le problème

Solution 1: Forcer les namespaces des deux proxy objects à êtr eles mêmes ( éventuellement supprimer les références à double ) 

  • avec ScvUtil : utiliser la commande /namespace sur chacun des services. example : SvcUtil.exe /namespace:*,BusinessServices.Common *.wsdl *.xsd /out:Reference.cs
  • supprimer à la main les types redondants
    • Il semblerait qu'il soit possible d'utiliser SvcUtil pour générer les références de tous les services d'un seul coup sans doublons.
  • modifier les fichiers app.config en conséquences pour prendre en compte les nouveaux namespaces

Solution 2: créer une dll que nous appellerons "MandatoryModuleHandler" qui contiendra les ServiceReferences sur le service exposant le type partagé. Le second service, qui consommera des instances de ce type, reste dans l'assembly principale.

  • Le client doit dans la configuration de ses propres ServicesReferences cocher la case "reuse types in specified referenced assemblies" et s'assurer que la dll MandatoryModuleHandler soit bien cochée dans la liste !
  • Comme le dit le texte, les types réutilisés sont recherchés uniquement dans les assemblies référencées, c'est pourquoi si le type existe dans l'assembly courante (donc pas de référence) le conflit de types persistera.
    • Il semblerait qu'il y'ait un bug dans la résolution des types appartenant à des assemblies référencées si celles-ci sont déjà compilées (références sur des fichiers dll) Pour palier à ce problème, il est nécessaire d'ajouter à la solution le projet servant à compiler la dll, de cette façon, les types qu'elle contient seront correctement résolus et utilisés pour le service qui voudra consommer ces types. Si les types ont déjà été résolus avec une référence de type projet, le remplacement de celle-ci par une référence sur une dll binaire ne reproduira cependant plus directement le bug précité.
  • Mettre à jour le app.config de l'application principale pour y copier toutes les informations sur les endpoints et bindings qui auront été générés dans le fichier app.config de la librairies MandatoryModuleHandler. Le fichier app.config de MandatoryModuleHandler pourra alors être effacé.

Conclusion 

Ces deux solutions ont chacune leur avantage :

  • La première ne crée pas de fichier .dll supplémentaire et/ou ne force pas à l'ajout du projet de cette dll dans la solution : on poura dès lors l'utiliser si l'on ne possède pas les sources d'une dll référençant un service.
  • La seconde permet de tout effectuer depuis Visual Studio sans avoir à utiliser d'outils en ligne de commande.