Frédéric Colin
WCF : L’extensibilité par la pratique – La base
Windows Communication Foundation offre des possibilités d’extensibilité intrinsèques très souvent méconnues. C’est pourquoi j’ai décidé d’en explorer un aspect, l’invocation, au travers d’un exemple très simple.
Par Frédéric Colin publié le 30/08/2009 à 23:14, lu 1830 fois, 6 pages
 2 | Un peu de théorie : l’extensibilité WCF
Le schéma suivant représente classiquement le pilier de base de toute application utilisant WCF, à savoir les Contrats, les Bindings et les Adresses du côté des clients et des services.
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image1.png
 
Si l’on creuse un peu plus techniquement le fonctionnement de WCF, un PROXY client est chargé de générer le message envoyé au service et de le transmettre au « Channel Stack » (cf. figure 2 ci-dessous). La « Channel Stack » est chargée de gérer les aspects transport, encodage, sécurité, fiabilité et transmission. Un jeu de boiboites et de technologies en quelque sorte, où il suffit déclarativement ou impérativement de paramétrer chaque étape de la pile d’exécution.
Du côté du Service, un « Channel Stack » (cf. figure 2, ci-dessous) paramétré à l’identique, est chargé de reconstituer le message depuis le canal de communication et de le transmettre au DISPATCHER WCF à des fins d’exécution du service appelé.
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image2.png
 
Nous avons donc là des parties bien distinctes permettant d’ajouter ce que j’appellerai des « points d’extensibilité », sur lesquels nous allons pouvoir injecter nos propres comportements. Je distinguerai notamment :
  • Les points d’extensibilité au niveau du PROXY (client)
  • Les points d’extensibilité au niveau du DISPATCHER (service).
Il va de soit que d’autre possibilités d’extensions existent (création de transports, d’encodages etc. spécifiques), mais elles sont en dehors du sujet de cette série article qui se consacrera au DISPATCHER (donc au niveau du service). Je n’aborderai pas non plus les possibilités d’extension au niveau du client (formatage, inspection de paramètres, etc.).
A partir de là, lorsque le message est récupéré au niveau du DISPATCHER, plusieurs points d’extensibilités existent et sont traités dans cet ordre :
  • L’inspection du message : appelé pour lire et modifier le contenu du message.
  • Par exemple pour valider un schéma XML indépendamment de l’opération à invoquer.
  • La sélection de l’opération : permet de connaitre le nom de l’opération à invoquer sur l’instance en cours.
  • Permet de sélectionner le nom de l’opération à exécuter sur l’instance du service concerné.
  • La désérialisation du message : cette partie est appelée lors de la sérialisation et la désérialisation du message.
  • Permet par exemple de gérer ses propres formats de sérialisation/désérialisation.
  • L’inspection des paramètres du message : utile pour lire et modifier les valeurs des paramètres.
  • Par exemple, imaginons que nous souhaitions mettre en place une vérification sur les valeurs des paramètres des méthodes afin de les valider ou non avant invocation.
  • L’invocation de l’opération : dernière étape du pipeline permettant de gérer l’invocation de l’opération sur l’instance courante.
  • L’exemple classique à ce niveau consiste à gérer un cache d’exécution des opérations et à rechercher dans ce dernier un résultat donné.
Dans cet article, nous nous focaliserons sur l’invocation d’opération. Pour cela, il faut suivre les étapes suivantes :
  • Création d’une Assembly dédiée à notre extensibilité.
  • Référencer « System.ServiceModel.dll ».
  • Utiliser l’espace de nom « System.ServiceModel.Description ».
  • A partir de là, nous allons créer deux classes : l’une chargée de la gestion de l’invocation et représentant une nouvelle extensibilité. L’autre chargée de l’injection de cette nouvelle extensibilité sur un service donné.
  • Création d’une classe implémentant l’interface « IOperationInvoker » pour l’implémentation technique de l’invocation de la méthode (à partir du message et de l’instance de l’objet supportant l’exécution de cette méthode).
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image3.png
 
L’implémentation de cette interface nous permet à partir d’un objet, d’une méthode à exécuter, d’un tableau de paramètres issus du message reçu par le DISPATCHER, d’invoquer ladite méthode avec les paramètres du tableau et de renvoyer à la fois les éventuels paramètres en sortie ainsi que l’éventuelle valeur de retour de l’appel.
En d’autres termes, cette interface permet à partir d’un objet non typé et d’un tableau de paramètres d’appeler une méthode fortement typée avec ses paramètres attendus et ce de manière synchrone (« Invoke ») ou asynchrone (« InvokeBegin », « InvokeEnd ») si ladite méthode doit être appelée de manière synchrone ou non (« IsSynchronous »).
Cette interface impose l’implémentation des contrats suivants :
  • Propriété « IsSynchronous » : la méthode à appeler doit-elle l’être de manière Synchrone ou non ?
  • Méthode « AllocateInputs » : permet d’instancier un tableau d’objet dont le nombre d’éléments est fonction du nombre de paramètres attendus par la méthode et dont les valeurs sont nulles par défaut.
  • Méthode « Invoke » : appel synchrone de la méthode sur l’objet.
  • Méthode « InvokeBegin » : implémentation asynchrone de l’invocation.
  • Méthode « InvokeEnd » : l’appel terminal de l’invocation asynchrone.
  • Création d’un attribut permettant d’injecter le comportement précédent sur une opération d’un service. Pour cela, nous créerons une classe héritant de « Attribute » et implémentant l’interface « IOperationBehavior »
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image4.png
 
Je ne reviendrai pas sur la création d’un attribut (héritage de la classe abstraite « Attribute »). Vous trouverez sur le Web moult explications sur le sujet !
L’implémentation de l’interface « IOperationBehavior » permet d’injecter à l’exécution, sur une opération d’un service, le comportement d’invocation créé précédemment et implémentant l’interface « IOperationInvoker ». A noter que ce comportement peut se charger des traitements à la fois du côté client et/ou du côté du service.
Ainsi, c’est grâce à ce comportement injecté au travers d’un attribut que nous allons pouvoir agir sur la création physique d’une instance d’un « invoker » donné. En définitive, ce point d’extensibilité intervient à un 3ième niveau après l’injection d’une extensibilité de service (« IServiceBehavior ») et l’injection d’une extensibilité de contrat (« IContractBehavior »). Comme à l’accoutumée avec WCF, cette injection d’extensibilité peut se faire de manière déclarative (attribut) ou bien de manière impérative (par programmation).
Cette interface impose l’implémentation des contrats suivants :
  • Méthode « AddBindingParameters » : cette méthode permettra d’agir au niveau des bindings utiles à l’exécution de notre invoker maison si besoin en ajoutant nos propres paramètres. Ne renvoie rien s’il n’y a aucune modification à faire.
  • Méthode « ApplyClientBehavior » : utile du côté du PROXY afin de récupérer, de modifier ou bien d’ajouter des extensions à tous les messages qui seront transmis à partir du client. Lorsque le comportement est uniquement du côté du DISPATCHER, il est de d’usage que cette méthode déclenche une exception (« NotImplementedException »).
  • Méthode « ApplyDispatchBehavior » : c’est l’appel à cette méthode par la Runtime WCF qui permettra d’exécuter notre propre comportement en lieu et place de celui initial pour une opération donnée.
  • Méthode « Validate » : l’appel par la Runtime WCF à cette méthode lui permettra de s’assurer que toutes les conditions requises à l’exécution sont respectées. Par exemple, la présence d’une certaine configuration, la vérification d’un certain niveau de sécurité, etc. Elle doit retourner une exception en cas d’invalidité confirmée de l’appel.
  • Injection du comportement créé précédemment au niveau de l’opération concernée. Par exemple pour un comportement appelé « FacadeOperationBehavior » et de manière déclarative :

[ServiceContract]

public interface IMonService

{

    [OperationContract]

    [FacadeOperationBehavior]

    void DoSomething();

}

 
» Démarrer une discussion