Introduction du développeur à Windows Communication Foundation 4
Aaron Skonnard, Pluralsight
Original : novembre 2009
Mise à jour vers RTM : avril 2010
Vue d’ensemble
.NET 4 est fourni avec de nouvelles fonctionnalités attrayantes et des améliorations bienvenues dans le domaine de Windows Communication Foundation (WCF). Ces améliorations WCF se concentrent principalement sur la simplification de l’expérience des développeurs, l’activation de scénarios de communication et la fourniture d’une intégration enrichie avec Windows Workflow Foundation (WF) en faisant des « services de flux de travail » un citoyen de première classe à l’avenir.
La bonne nouvelle est que la plupart des modifications de WCF 4 se concentrent sur la simplification des scénarios courants d’aujourd’hui et la création de nouveaux scénarios de communication et de nouveaux styles de développement. Par conséquent, le déplacement de vos solutions WCF existantes vers .NET 4 sera assez fluide en termes de migration. Ensuite, vous décidez simplement des fonctionnalités WCF 4 dont vous souhaitez tirer parti dans vos solutions à l’avenir. Le reste de cet article vous présente chacune des nouvelles zones de fonctionnalités WCF 4 et montre comment elles fonctionnent.
Nouveautés de WCF 4
WCF 4 est fourni avec un large éventail de fonctionnalités spécifiques, mais la figure 1 décrit les main domaines de fonctionnalités sur lesquels nous allons nous concentrer tout au long de l’article ci-dessous. Ces zones de fonctionnalités résument la plupart des nouveautés de WCF 4 et mettent en évidence les opportunités de niveau supérieur offertes par cette version du .NET Framework.
Figure 1 : Zones de fonctionnalités WCF 4
Domaine de fonctionnalités | Description |
---|---|
Configuration simplifiée |
Simplification de la section de configuration WCF par le biais de la prise en charge des points de terminaison par défaut, des configurations de liaison et de comportement. Ces modifications permettent d’héberger des services sans configuration, ce qui simplifie considérablement l’expérience des développeurs pour les scénarios WCF les plus courants. |
Découverte |
Nouvelle prise en charge de l’infrastructure pour les comportements de découverte de services ad hoc et managés, qui sont conformes au protocole de WS-Discovery standard. |
Service de routage |
Nouvelle prise en charge du framework pour un service de routage configurable que vous pouvez utiliser dans vos solutions WCF. Fournit des fonctionnalités pour le routage basé sur le contenu, le pontage de protocole et la gestion des erreurs. |
Améliorations apportées à REST |
Améliorations apportées aux services WebHttp WCF avec des fonctionnalités et des outils supplémentaires qui simplifient le développement du service REST. |
Services de workflow |
Prise en charge d’infrastructure enrichie pour l’intégration de WCF à WF afin d’implémenter des services de flux de travail déclaratifs à long terme. Ce nouveau modèle de programmation vous offre les meilleures fonctionnalités des deux frameworks (WCF & WF). |
Une fois que nous avons terminé de couvrir ces main domaines de fonctionnalités, nous aborderons brièvement certaines des fonctionnalités WCF de niveau inférieur plus avancées fournies avec .NET 4, notamment des fonctionnalités améliorées de résolution de type, la prise en charge des files d’attente avec des consommateurs concurrents (« contexte de réception »), la prise en charge des données binaires non exploitées via un encodeur de flux d’octets et la prise en charge du suivi basé sur ETW hautes performances.
Lorsque nous aurons terminé, vous verrez que WCF 4 devient plus facile à utiliser et offre une prise en charge plus intégrée de certains des scénarios et styles de développement les plus courants d’aujourd’hui.
Configuration simplifiée
Le modèle de programmation unifié proposé par WCF dans la version 3.x est à la fois une bénédiction et une malédiction : il simplifie l’écriture de la logique de service pour différents scénarios de communication, mais il augmente également la complexité du côté de la configuration des choses, car il fournit tant d’options de communication sous-jacentes différentes que vous êtes obligé de comprendre avant de commencer.
La réalité est que la configuration WCF devient généralement le domaine le plus coûteux de l’utilisation de WCF dans la pratique aujourd’hui et une grande partie de cette complexité incombe au personnel informatique/opérations qui n’est pas préparé à y faire face.
Compte tenu de cette réalité, quand vous considérez la complexité nette de l’utilisation de WCF 3.x, on peut raisonnablement conclure qu’il est plus difficile à utiliser que son prédécesseur ASP.NET services Web (ASMX). Avec ASMX, vous avez pu définir une opération [WebMethod] et le runtime a fourni automatiquement une configuration par défaut pour les communications sous-jacentes. En revanche, lorsqu’ils passent à WCF 3.x, les développeurs doivent connaître suffisamment les différentes options de configuration WCF pour définir au moins un point de terminaison. Et le nombre intimidant d’options de configuration fait souvent peur à certains développeurs.
Dans un effort pour rendre l’expérience WCF globale aussi simple qu’ASMX, WCF 4 est fourni avec un nouveau modèle de « configuration par défaut » qui supprime complètement la nécessité de toute configuration WCF. Si vous ne fournissez aucune configuration WCF pour un service particulier, le runtime WCF 4 configure automatiquement votre service avec des points de terminaison standard et des configurations de liaison/comportement par défaut. Cela facilite grandement la mise en service et l’exécution d’un service WCF, en particulier pour ceux qui ne sont pas familiarisés avec les différentes options de configuration WCF et qui sont heureux d’accepter les valeurs par défaut, du moins pour commencer.
Points de terminaison par défaut
Avec WCF 3.x, si vous essayez d’héberger un service sans point de terminaison configuré, le instance ServiceHost lève une exception vous informant que vous devez configurer au moins un point de terminaison. Avec WCF 4, ce n’est plus le cas, car le runtime ajoute automatiquement un ou plusieurs « points de terminaison par défaut » pour vous, rendant ainsi le service utilisable sans aucune configuration.
Voici comment cela fonctionne. Lorsque l’application hôte appelle Open sur le instance ServiceHost, elle génère la description du service interne à partir du fichier de configuration de l’application, ainsi que tout ce que l’application hôte a configuré explicitement et, si le nombre de points de terminaison configurés est toujours égal à zéro, elle appelle AddDefaultEndpoints, une nouvelle méthode publique trouvée dans la classe ServiceHost. Cette méthode ajoute un ou plusieurs points de terminaison à la description du service en fonction des adresses de base du service (dans les scénarios IIS, il s’agit de l’adresse .svc). Étant donné que la méthode est publique, vous pouvez également l’appeler directement dans des scénarios d’hébergement personnalisés.
Pour être précis, l’implémentation de AddDefaultEndpoints ajoute un point de terminaison par défaut par adresse de base pour chaque contrat de service implémenté par le service. Par exemple, si le service implémente deux contrats de service et que vous configurez l’hôte avec une seule adresse de base, AddDefaultEndpoints configure le service avec deux points de terminaison par défaut (un pour chaque contrat de service). Toutefois, si le service implémente deux contrats de service et que l’hôte est configuré avec deux adresses de base (une pour HTTP et une pour TCP), AddDefaultEndpoints configure le service avec quatre points de terminaison par défaut.
Examinons un exemple complet pour illustrer ce travail. Supposons que vous disposez des contrats de service WCF suivants et de l’implémentation de service suivante :
[ServiceContract]
interface publique IHello
{
[OperationContract]
void SayHello(string name);
}
[ServiceContract]
interface publique IGoodbye
{
[OperationContract]
void SayGoodbye(string name);
}
classe publique GreetingService : IHello, IGoodbye // service implémente les deux contrats
{
public void SayHello(string name)
{
Console.WriteLine(« Hello {0}« , nom);
}
public void SayGoodbye(string name)
{
Console.WriteLine(« Goodbye {0}« , name);
}
}
Avec WCF 4, vous pouvez désormais utiliser ServiceHost pour héberger le service GreetingService sans aucune configuration d’application. Lorsque vous utilisez ServiceHost dans des scénarios d’hébergement personnalisé, vous devez spécifier une ou plusieurs adresses de base à utiliser. L’exemple suivant montre comment héberger greetingservice dans une application console, et là encore, vous pouvez supposer qu’aucun fichier app.config n’est associé à ce programme :
programme de classe
{
static void Main(string[] args)
{
L’hôte est configuré avec deux adresses de base, l’une pour HTTP et l’autre pour TCP
Hôte ServiceHost = new ServiceHost(typeof(GreetingService),
new URI(« https://localhost:8080/greeting"),
new Uri(« net.tcp://localhost:8081/greeting »));
Hôte. Open();
foreach (ServiceEndpoint se dans l’hôte. Description.Endpoints)
Console.WriteLine(« A: {0}, B: {1}, C: {2}« ,
se. Adresse, se.Binding.Name, se.Contract.Name);
Console.WriteLine(« Appuyez sur <Entrée> pour arrêter le service. »);
Console.ReadLine();
Hôte. Close();
}
}
Cet exemple configure ServiceHost avec deux adresses de base : une pour HTTP et une autre pour TCP. Lorsque vous exécutez ce programme, vous voyez quatre points de terminaison imprimés dans la fenêtre de console, comme illustré dans la figure 2. Vous en obtiendrez deux pour l’adresse de base HTTP, une par contrat et deux pour l’adresse de base TCP, une fois de plus une par contrat. Tout cela est fourni en arrière-plan par le instance ServiceHost.
Figure 2 : Points de terminaison par défaut affichés dans la fenêtre de console
Notez que WCF choisit d’utiliser BasicHttpBinding pour les points de terminaison HTTP par défaut et NetTcpBinding pour les points de terminaison TCP par défaut. Je vais vous montrer comment modifier ces valeurs par défaut sous peu.
N’oubliez pas que ce comportement de point de terminaison par défaut ne s’affiche que lorsque le service n’a pas été configuré avec des points de terminaison. Si je modifie l’application console pour configurer le service avec au moins un point de terminaison, aucun de ces points de terminaison par défaut n’apparaît dans la sortie. Pour illustrer cela, je vais simplement ajouter la ligne de code suivante qui appelle AddServiceEndpoint après avoir construit le instance ServiceHost :
...
Hôte ServiceHost = new ServiceHost(typeof(GreetingService),
new URI(« https://localhost:8080/greeting"),
new Uri(« net.tcp://localhost:8081/greeting »));
Hôte. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), « myendpoint »);
...
Si vous exécutez l’application console avec cette ligne de code insérée, vous remarquerez qu’un seul point de terminaison apparaît désormais dans la sortie, celui que nous avons configuré manuellement dans le code ci-dessus (voir la figure 3).
Figure 3 : Sortie de console après la configuration de l’hôte avec un seul point de terminaison
Toutefois, vous pouvez toujours appeler AddDefaultEndpoints vous-même si vous souhaitez toujours ajouter l’ensemble de points de terminaison par défaut avec les vôtres. L’exemple de code suivant montre comment procéder :
...
Hôte ServiceHost = new ServiceHost(typeof(GreetingService),
new URI(« https://localhost:8080/greeting"),
new Uri(« net.tcp://localhost:8081/greeting »));
Hôte. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), « myendpoint »);
Hôte. AddDefaultEndpoints();
...
Si vous réexécutez l’application console avec cette modification, cinq points de terminaison s’affichent dans la fenêtre de console , celui que j’ai configuré manuellement avec les quatre points de terminaison par défaut (voir figure 4).
Figure 4 : Sortie de console après l’appel manuel d’AddDefaultEndpoints
Maintenant que nous comprenons l’algorithme et les mécanismes d’ajout de points de terminaison par défaut aux services au moment de l’exécution, la question suivante est comment WCF décide-t-il de la liaison à utiliser pour une adresse basée particulière ?
Mappage de protocole par défaut
La réponse à cette question est simple. WCF définit un mappage de protocole par défaut entre les schémas de protocole de transport (par exemple, http, net.tcp, net.pipe, etc.) et les liaisons WCF intégrées. Le mappage de protocole par défaut se trouve dans le fichier .NET 4 machine.config.comments et il ressemble à ceci :
<system.serviceModel>
<protocolMapping>
<add scheme="http » binding="basicHttpBinding » bindingConfiguration=" » />
<add scheme="net.tcp » binding="netTcpBinding » bindingConfiguration=""/>
<add scheme="net.pipe » binding="netNamedPipeBinding » bindingConfiguration=""/>
<add scheme="net.msmq » binding="netMsmqBinding » bindingConfiguration=""/>
</protocolMapping>
...
Vous pouvez remplacer ces mappages au niveau de l’ordinateur en ajoutant cette section à machine.config et en modifiant le mappage pour chaque schéma de protocole. Ou si vous souhaitez uniquement le remplacer dans l’étendue d’une application, vous pouvez remplacer cette section dans votre fichier de configuration application/web.
Par exemple, si votre organization est principalement axé sur la création de services RESTful avec WCF, il peut être judicieux de modifier la liaison par défaut pour le schéma de protocole « http » en WebHttpBinding. L’exemple suivant montre comment effectuer cette opération dans un fichier de configuration d’application :
<configuration>
<system.serviceModel>
<protocolMapping>
<add scheme="http » binding="webHttpBinding"/>
</protocolMapping>
</system.serviceModel>
</Configuration>
Maintenant, si je réexécute l’application console indiquée précédemment avec cette app.config en place, les deux points de terminaison HTTP par défaut indiquent maintenant qu’ils utilisent WebHttpBinding (voir figure 5).
Figure 5 : Sortie de console après avoir substitué le mappage de protocole HTTP par défaut
Une fois que WCF a déterminé la liaison à utiliser via la table de mappage de protocole, il utilise la configuration de liaison par défaut lors de la configuration du point de terminaison par défaut. Si vous n’êtes pas satisfait des valeurs par défaut de liaison intégrées, vous pouvez également remplacer la configuration par défaut pour une liaison particulière.
Configurations de liaison par défaut
Chaque liaison WCF est fournie avec une configuration par défaut qui est utilisée, sauf si l’application hôte est explicitement remplacée par l’application hôte pour un point de terminaison particulier. Chaque liaison instance que vous utilisez est toujours fournie avec les valeurs par défaut intégrées, sauf si vous choisissez de remplacer en appliquant une configuration de liaison explicite.
Dans WCF 3.x, pour ce faire, définissez une configuration de liaison nommée que vous pouvez appliquer aux définitions de point de terminaison via l’attribut bindingConfiguration. Les mécanismes de ce bon fonctionnement sont fastidieux et sujettes aux erreurs. Le fichier de configuration suivant présente un exemple classique :
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicWithMtom » messageEncoding="Mtom"/>
</basicHttpBinding>
</Liaisons>
<services>
<service name="GreetingService »>
<endpoint address="mtom » binding="basicHttpBinding »
bindingConfiguration="BasicWithMtom »
contract="IHello"/>
</service>
</Services>
</system.serviceModel>
</Configuration>
Dans l’exemple ci-dessus, la configuration de liaison « BasicWithMtom » remplace les valeurs par défaut de BasicHttpBinding en remplaçant l’encodage des messages par MTOM. Toutefois, cette configuration de liaison prend effet uniquement lorsque vous l’appliquez à un point de terminaison spécifique par le biais de l’attribut « bindingConfiguration », c’est l’étape qui échappe souvent aux développeurs et au personnel des opérations, ce qui entraîne des problèmes de configuration.
Avec WCF 4, vous pouvez désormais définir des configurations de liaison par défaut en omettant simplement le nom de la configuration de liaison lors de la définition de la nouvelle configuration. Wcf utilisera ensuite cette configuration par défaut pour tous les points de terminaison utilisant cette liaison qui n’ont pas de configuration de liaison explicite définie sur eux.
Par exemple, si nous ajoutons le fichier app.config suivant à l’application console indiquée précédemment, les deux points de terminaison HTTP par défaut récupèrent cette configuration BasicHttpBinding par défaut, qui active MTOM :
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding messageEncoding="Mtom"/><-- notez qu’il n’existe aucun attribut name -->
</basicHttpBinding>
</Liaisons>
</system.serviceModel>
</Configuration>
Bien sûr, vous pouvez également ajouter ces configurations de liaison par défaut à machine.config si vous souhaitez qu’elles prennent effet sur tous les services s’exécutant sur l’ordinateur ou que vous pouvez les définir sur une base application par application en ajoutant les configurations de liaison par défaut dans le fichier de configuration de l’application.
Cette fonctionnalité vous offre un mécanisme simple pour définir un ensemble standard de valeurs de liaison par défaut que vous pouvez utiliser dans tous vos services sans imposer la complexité des configurations de liaison à d’autres développeurs ou au personnel informatique/des opérations. Ils peuvent simplement choisir la liaison appropriée et être assurés que la configuration par défaut appropriée sera fournie par l’environnement d’hébergement.
En plus des configurations de liaison par défaut, l’autre élément à prendre en compte pour vos services et points de terminaison est la configuration de leur comportement par défaut.
Configurations de comportement par défaut
WCF 4 permet également de définir des configurations de comportement par défaut pour les services et les points de terminaison, ce qui peut simplifier les choses lorsque vous souhaitez partager une configuration de comportement par défaut standard entre tous les services ou points de terminaison s’exécutant sur une machine ou au sein d’une solution.
Dans WCF 3.x, vous devez définir des configurations de comportement nommé que vous appliquez explicitement aux services et aux points de terminaison via l’attribut « behaviorConfiguration ». Avec WCF 4, vous pouvez définir des configurations de comportement par défaut en omettant le nom dans la définition de configuration. Si vous ajoutez ces comportements par défaut à machine.config, ils s’appliquent à tous les services ou points de terminaison hébergés sur l’ordinateur. Si vous les ajoutez à app.config, elles prendront effet uniquement dans l’étendue de l’application hôte. Voici un exemple :
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<comportement><-- notez aucun attribut de nom -->
<serviceMetadata httpGetEnabled="true"/>
</Comportement>
</serviceBehaviors>
</Comportements>
</system.serviceModel>
</Configuration>
Cet exemple montre comment activer les métadonnées de service pour tout service qui n’est pas accompagné d’une configuration de comportement explicite. Si nous ajoutons cette configuration de comportement par défaut au fichier app.config de l’application console affiché précédemment et que nous réexécutons l’application, nous pouvons accéder à l’adresse HTTP de base pour récupérer la page d’aide du service et la définition WSDL du service (voir figure 6).
Figure 6 : Navigation vers les métadonnées de service activées par la configuration de comportement par défaut
Une autre nouvelle fonctionnalité de WCF 4 est que les configurations de comportement prennent désormais en charge un modèle d’héritage. Si une application définit une configuration de comportement en utilisant le même nom que celui déjà défini dans machine.config, la configuration de comportement spécifique à l’application est fusionnée avec la configuration à l’échelle de l’ordinateur, ce qui ajoute des comportements supplémentaires à la configuration de comportement composite dérivée.
Points de terminaison standard
Une autre nouvelle fonctionnalité WCF 4 associée aux points de terminaison par défaut est appelée « points de terminaison standard ». Vous pouvez considérer un point de terminaison standard comme une définition de point de terminaison préconfigurée commune intégrée à l’infrastructure WCF 4 que vous pouvez simplement utiliser. Les points de terminaison standard définissent une configuration de point de terminaison « standard » que vous ne modifiez généralement pas, même si vous le souhaitez, comme vous le verrez sous peu.
La figure 7 décrit les points de terminaison standard fournis avec WCF 4. Celles-ci fournissent des définitions de point de terminaison standard pour certaines des fonctionnalités et scénarios de communication WCF 4 les plus courants. Par exemple, dans le cas d’un point de terminaison MEX, vous devrez toujours spécifier IMetadataExchange pour le contrat de service et choisissez probablement HTTP. Ainsi, au lieu de vous forcer à toujours le faire manuellement, WCF fournit une définition de point de terminaison standard pour l’échange de données metdata appelée « mexEndpoint » qui est facile à utiliser.
Figure 7 : Points de terminaison standard dans WCF 4
Nom du point de terminaison standard | Description |
---|---|
mexEndpoint |
Définit un point de terminaison standard pour MEX configuré avec IMetadataExchange pour le contrat de service, mexHttpBinding comme liaison par défaut (vous pouvez modifier cela) et une adresse vide. |
dynamicEndpoint |
Définit un point de terminaison standard configuré pour utiliser la découverte WCF dans une application cliente WCF. Lors de l’utilisation de ce point de terminaison standard, une adresse n’est pas requise, car lors du premier appel, le client recherche un point de terminaison de service correspondant au contrat spécifié et s’y connecte automatiquement pour vous. Par défaut, la requête de découverte est envoyée via UDP multidiffusion, mais vous pouvez spécifier la liaison de découverte et les critères de recherche à utiliser lorsque vous en avez besoin. |
discoveryEndpoint |
Définit un point de terminaison standard préconfiguré pour les opérations de découverte au sein d’une application cliente. L’utilisateur doit spécifier l’adresse et la liaison lors de l’utilisation de ce point de terminaison standard. |
UdpDiscoveryEndpoint |
Définit un point de terminaison standard préconfiguré pour les opérations de découverte au sein d’une application cliente à l’aide de la liaison UDP à une adresse de multidiffusion. Dérive de DiscoveryEndpoint. |
announcementEndpoint |
Définit un point de terminaison standard préconfiguré pour la fonctionnalité d’annonce de la découverte. L’utilisateur doit spécifier l’adresse et la liaison lors de l’utilisation de ce point de terminaison standard. |
udpAnnouncementEndpoint |
Définit un point de terminaison standard préconfiguré pour la fonctionnalité d’annonce sur une liaison UDP à une adresse de multidiffusion. Ce point de terminaison dérive de announcementEndpoint. |
workflowControlEndpoint |
Définit un point de terminaison standard permettant de contrôler l'exécution d'instances de flux de travail (créer, exécuter, interrompre, arrêter, etc.). |
webHttpEndpoint |
Définit un point de terminaison standard configuré avec WebHttpBinding et WebHttpBehavior. Utilisez pour exposer les services REST. |
webScriptEndpoint |
Définit un point de terminaison standard configuré avec WebHttpBinding et WebScriptEnablingBehavior. Utilisez pour exposer les services Ajax. |
Vous pouvez tirer parti de l’un de ces points de terminaison standard dans vos propres configurations de service en les référençant simplement par nom. L’élément <endpoint> est désormais fourni avec un attribut « kind » que vous pouvez utiliser pour spécifier le nom d’un point de terminaison standard. Par instance, l’exemple suivant configure GreetingService avec un point de terminaison MEX en tirant parti de la définition standard « mexEndpoint » :
<configuration>
<system.serviceModel>
<services>
<service name="GreetingService »>
<endpoint kind="basicHttpBinding » contract="IHello"/>
<endpoint kind="mexEndpoint » address="mex » />
</service>
</Services>
</system.serviceModel>
</Configuration>
Bien que les points de terminaison standard vous protègent de la plupart des détails de configuration (par exemple, avec le mexEndpoint, je n’ai pas eu à spécifier la liaison ou le contrat), il se peut que vous souhaitiez toujours les utiliser, mais que vous deviez configurer les définitions de point de terminaison standard un peu différemment.
Lorsque vous devez effectuer cette opération, vous pouvez utiliser la <section standardEndpoints> et remplacer la configuration du point de terminaison pour le point de terminaison standard. Vous pouvez ensuite référencer cette configuration lors de la définition d’un nouveau <point de terminaison> via l’attribut endpointConfiguration, comme illustré ici :
<configuration>
<system.serviceModel>
<services>
<service name="GreetingService »>
<endpoint binding="basicHttpBinding » contract="IHello"/>
<endpoint kind="udpDiscoveryEndpoint » endpointConfiguration="D11"/>
</service>
</Services>
<standardEndpoints>
<udpDiscoveryEndpoint>
<standardEndpoint name="D11 » discoveryVersion="WSDiscovery11"/>
</udpDiscoveryEndpoint>
</standardEndpoints>
<behaviors>
<serviceBehaviors>
<Comportement>
<serviceDiscovery/>
<serviceMetadata httpGetEnabled="true"/>
</Comportement>
</serviceBehaviors>
</Comportements>
</system.serviceModel>
</Configuration>
Cet exemple montre comment modifier la version par défaut WS-Discovery pour le point de terminaison standard nommé « udpDiscoveryEndpoint » (nous allons en savoir plus sur la découverte du service sous peu).
Simplification de l’hébergement IIS/ASP.NET
Compte tenu de ces nouvelles fonctionnalités pour les points de terminaison par défaut, les configurations de liaison par défaut et les configurations de comportement par défaut, l’hébergement dans IIS/ASP.NET devient beaucoup plus facile dans WCF 4. ASP.NET les développeurs habitués à travailler avec les services ASMX peuvent désormais définir des services WCF qui sont tout aussi simples par nature.
En fait, case activée la simplicité de la définition de service WCF suivante :
<-- HelloWorld.svc -->
<%@ ServiceHost Language="C# » Debug="true » Service="HelloWorldService
CodeBehind="~/App_Code/HelloWorldService.cs » %>
[ServiceContract]
classe publique HelloWorldService
{
[OperationContract]
chaîne publique HelloWorld()
{
retourner « hello, world »;
}
}
Il s’agit de la forme la plus simple de définition de service WCF, car nous n’utilisons pas une définition d’interface distincte pour définir le contrat de service et tout est défini dans un seul fichier, HelloWorld.svc (remarque : je ne recommande pas cette approche, notant simplement qu’il est possible de faire une comparaison avec ASMX). Cela doit ressembler beaucoup aux services ASMX classiques, la principale différence étant les noms d’attributs que vous utilisez sur la classe de service (par exemple, [WebService] et [WebMethod]). Il y a certainement moins de pièces mobiles.
Avec les nouvelles fonctionnalités WCF 4 décrites dans la section précédente, vous pouvez maintenant accéder à HelloWorld.svc sans configuration WCF supplémentaire et la logique d’activation WCF crée le ServiceHost instance en arrière-plan et le configure avec un seul point de terminaison HTTP par défaut. Et si vous avez ajouté un comportement de service par défaut à votre fichier machine.config qui active les métadonnées du service, vous verrez la page d’aide WCF et le lien vers la définition WSDL lorsque vous accédez à HelloWorld.svc (voir figure 8).
Figure 8 : page d’aide HelloWorldService
Si vous n’avez pas activé les métadonnées de service à l’échelle de l’ordinateur, vous pouvez les activer dans votre application web en ajoutant la configuration de comportement par défaut suivante à votre fichier web.config :
...
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<comportement><-- notez qu’il n’y a pas d’attribut de nom -->
<serviceMetadata httpGetEnabled="true"/>
</Comportement>
</serviceBehaviors>
</Comportements>
</system.serviceModel>
...
Vous pouvez également modifier d’autres paramètres par défaut en suivant les procédures décrites dans les sections précédentes. Par exemple, vous pouvez modifier le mappage de protocole par défaut, ajouter des configurations de liaison par défaut ou des configurations de comportement par défaut supplémentaires. Si le service implémente plusieurs contrats de service, l’instance ServiceHost résultant est configuré avec un point de terminaison HTTP par contrat.
Par exemple, supposons que nous hébergeons notre GreetingService (à partir de précédemment) via le fichier .svc présenté ici :
<-- GreetingService.svc -->
<%@ServiceHost Service="GreetingService"%>
Compte tenu de notre définition de GreetingService, la première fois que vous accédez à GreetingService.svc, la logique d’activation WCF crée l’instance ServiceHost et ajoute deux points de terminaison HTTP par défaut pour le type GreetingService (un pour chaque contrat de service). Vous pouvez le vérifier en accédant à la définition WSDL et vous trouverez deux <éléments de port> dans l’élément de <service> .
Dans l’ensemble, ces simplifications de configuration WCF devraient permettre aux développeurs ASP.NET d’obtenir plus facilement des services WCF opérationnels dans leurs applications web, et cela rapproche le cas le plus simple de l’expérience utilisée par les développeurs avec ASP.NET services Web.
Activation sans fichier
Bien que les fichiers .svc facilitent l’exposition des services WCF, une approche encore plus simple consiste à définir des points de terminaison d’activation virtuelle dans Web.config, ce qui élimine complètement la nécessité de fichiers .svc.
Dans WCF 4, vous pouvez définir des points de terminaison d’activation de service virtuel qui correspondent à vos types de services dans Web.config. Cela permet d’activer les services WCF sans avoir à gérer les fichiers .svc physiques (c’est-à-dire « activation sans fichier »). L’exemple suivant montre comment configurer un point de terminaison d’activation :
<configuration>
<system.serviceModel>
<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="Greeting.svc » service="GreetingService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
</Configuration>
Une fois ce paramètre en place, il est désormais possible d’activer GreetingService à l’aide d’un chemin relatif de « Greeting.svc » (par rapport à l’adresse de base de l’application web). Pour illustrer cela, j’ai créé une application IIS sur mon ordinateur appelée « GreetingSite », que j’ai affectée au pool d’applications « ASP.NET v4.0 », et l’ai mappée au répertoire du projet GreetingService qui contient les web.config illustrées ci-dessus. Maintenant, je peux simplement accéder à https://localhost/GreetingSite/Greeting.svc sans avoir réellement un fichier .svc physique sur le disque. La figure 9 montre à quoi cela ressemble dans le navigateur.
Figure 9 : Exemple d’activation sans fichier
Découverte
La prochaine fonctionnalité majeure de WCF 4 que nous allons aborder est la découverte de service. Dans certains environnements spécialisés orientés service, il existe des services dont l’emplacement d’exécution est dynamique et en constante évolution. Par exemple, prenons l’exemple d’environnements où différents types d’appareils compatibles avec le service rejoignent et quittent constamment le réseau dans le cadre de la solution métier globale. Pour faire face à cette réalité, les clients doivent découvrir dynamiquement l’emplacement d’exécution des points de terminaison de service.
WS-Discovery est une spécification OASIS qui définit un protocole SOAP pour la découverte dynamique de l’emplacement des points de terminaison de service au moment de l’exécution. Le protocole permet aux clients de rechercher des points de terminaison de service qui correspondent à certains critères afin de récupérer une liste de candidats appropriés. Un client peut ensuite choisir un point de terminaison spécifique dans la liste découverte et utiliser son adresse de point de terminaison d’exécution actuelle.
WS-Discovery définit deux modes de fonctionnement principaux : le mode ad hoc et le mode managé. En mode ad hoc, les clients recherchent les services en envoyant des messages de multidiffusion. L’infrastructure fournit un mécanisme de multidiffusion UDP pour ce mode ad hoc. Les services qui correspondent à la sonde répondent directement au client. Afin de réduire le besoin d’interrogation des clients, les services peuvent également « s’annoncer » lorsqu’ils rejoignent ou quittent le réseau en envoyant un message de multidiffusion aux clients qui peuvent être « à l’écoute ». La découverte ad hoc est limitée par le protocole utilisé pour la multidiffusion des messages. Dans le cas d’UDP, seuls les services à l’écoute sur le sous-réseau local pourront recevoir les messages.
Avec la découverte de services managés, vous fournissez un proxy de découverte sur le réseau qui « gère » les points de terminaison de service détectables. Les clients communiquent directement avec le proxy de découverte pour localiser les services en fonction de critères de sondage. Le proxy de découverte a besoin d’un référentiel de services qu’il peut mettre en correspondance avec la requête. La façon dont le proxy est rempli avec ces informations est un détail d’implémentation. Les proxys de découverte peuvent facilement être connectés à un référentiel de services en cours d’affichage, ils peuvent être préconfigurés avec une liste de points de terminaison, ou un proxy de découverte peut même écouter les annonces pour mettre à jour son cache. En mode managé, les annonces peuvent être monodiffusions directement sur un destinataire, éventuellement par un proxy de découverte.
L’infrastructure .NET 4.0 fournit les classes de base dont vous avez besoin pour implémenter votre propre proxy de découverte. Les classes de base éliminent les détails du protocole de découverte pour vous permettre de vous concentrer simplement sur la logique que le proxy de découverte doit contenir. Par exemple, vous devez uniquement définir ce que le proxy de découverte fera en réponse à un message de sonde, à des messages d’annonce et à des messages de résolution.
WCF 4 fournit une implémentation complète du protocole WS-Discovery et prend en charge les modes de découverte ad hoc et managé. Nous allons examiner brièvement chacun de ces éléments ci-dessous.
Découverte de service simple
Le moyen le plus simple d’activer la découverte de service consiste à utiliser le mode ad hoc. WCF facilite l’activation de la découverte de services dans vos applications hôtes de service en fournissant des points de terminaison de découverte standard et un comportement de découverte de service. Pour configurer votre service pour la découverte, ajoutez simplement le point de terminaison « udpDiscoveryEndpoint » standard, puis activez le <comportement serviceDiscovery> sur le service.
Voici un exemple complet illustrant comment procéder :
<configuration>
<system.serviceModel>
<services>
<service name="CalculatorService »>
<endpoint binding="wsHttpBinding » contract="ICalculatorService » />
<-- ajouter un point de terminaison de découverte UDP standard :>
<endpoint name="udpDiscovery » kind="udpDiscoveryEndpoint"/>
</service>
</Services>
<behaviors>
<serviceBehaviors>
<Comportement>
<serviceDiscovery/><-- activer le comportement de découverte de service :>
</Comportement>
</serviceBehaviors>
</Comportements>
</system.serviceModel>
</Configuration>
En procédant ainsi, votre service devient détectable via UDP sur le sous-réseau local. Les clients peuvent ensuite tirer parti de WS-Discovery au moment de l’exécution pour « découvrir » l’adresse réelle du service en cours d’exécution. WCF 4 permet aux clients d’effectuer facilement cette opération via le point de terminaison standard dynamicEndpoint.
Il vous suffit de prendre votre point de terminaison client existant que vous utilisiez pour vous connecter au service, de supprimer l’adresse et d’ajouter une balise kind="dynamicEndpoint ».
<configuration>
<system.serviceModel>
<client>
<Terminaison
name="calculatorEndpoint »
kind="dynamicEndpoint »
binding="wsHttpBinding »
contract="ICalculatorService »>
</Terminaison>
</Client>
</system.serviceModel>
</Configuration>
Lorsque le premier appel de service est effectué, le client envoie une requête de multidiffusion à la recherche de services qui correspondent au contrat ICalculatorService et tente de s’y connecter. Différents paramètres vous permettent d’ajuster votre serach, d’ajuster les liaisons de découverte et de contrôler le processus de découverte. Vous pouvez également effectuer tout cela par programmation à l’aide de la classe DiscoveryClient.
L’exemple suivant va plus loin en montrant comment utiliser udpDiscoveryEndpoint par programmation pour découvrir un point de terminaison ICalculatorService, puis l’appeler :
Créer DiscoveryClient
DiscoveryClient discoveryClient =
new DiscoveryClient(new UdpDiscoveryEndpoint());
Rechercher les points de terminaison ICalculatorService dans l’étendue spécifiée
FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));
FindResponse findResponse = discoveryClient.Find(findCriteria);
Il vous suffit de choisir le premier point de terminaison découvert
EndpointAddress address = findResponse.Endpoints[0]. Adresse;
Créer le client de service cible
CalculatorServiceClient client =
new CalculatorServiceClient(« calculatorEndpoint »);
Se connecter au point de terminaison de service découvert
Client. Endpoint.Address = address;
Console.WriteLine(« Invoking CalculatorService at {0}« , address);
Appelez l’opération Ajouter un service.
double result = client. Add(100, 15.99);
Console.WriteLine(« Add({0},{1}) = {2}« , 100, 15.99, result);
Une fois que le programme client a récupéré la collection de points de terminaison découverts, il peut utiliser l’un d’entre eux pour appeler réellement le service cible. La figure 10 montre la sortie de l’exécution du code client indiqué ci-dessus en supposant que le service s’exécute également en même temps. Remarque : dans cet exemple, l’opération Find sur le client de découverte est synchrone ; La découverte prend également en charge les opérations de recherche asynchrones.
Figure 10 : Sortie de l’exécution du code client de découverte
Utilisation d’étendues lors de la découverte de points de terminaison
Dans l’exemple précédent, le client a simplement sondé les services en fonction du type de contrat de service. Les clients peuvent affiner les résultats de la découverte en fournissant des informations d’étendue supplémentaires lors de l’envoi des sondes de découverte. Examinons un exemple simple pour voir comment les « étendues » peuvent être utilisées lors de la découverte.
Tout d’abord, le service doit associer une ou plusieurs étendues à chaque point de terminaison qu’il va publier pour la découverte. WCF 4 est fourni avec un <comportement endpointDiscovery> que vous pouvez utiliser pour définir un ensemble d’étendues que vous pouvez associer à une définition de point de terminaison. L’exemple suivant montre comment associer deux étendues au point de terminaison unique défini sur le service :
<configuration>
<system.serviceModel>
<services>
<service name="CalculatorService »
behaviorConfiguration="calculatorServiceBehavior »>
<endpoint binding="wsHttpBinding »
contract="ICalculatorService »
behaviorConfiguration="ep1Behavior » />
<endpoint name="udpDiscovery » kind="udpDiscoveryEndpoint"/>
</service>
</Services>
<behaviors>
<serviceBehaviors>
<behavior name="calculatorServiceBehavior »>
<serviceDiscovery/>
</Comportement>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="ep1Behavior »>
<endpointDiscovery>
<-- étendues associées à ce comportement de point de terminaison :>
<scopes>
<add scope= »http://www.example.org/calculator"/>
<add scope="ldap:///ou=engineering,o=exampleorg,c=us"/>
</Étendues>
</endpointDiscovery>
</Comportement>
</endpointBehaviors>
</Comportements>
</system.serviceModel>
</Configuration>
Les clients peuvent rechercher des points de terminaison de service en fonction d’étendues spécifiques au moment de l’exécution. Pour ce faire, ils peuvent ajouter une liste d’étendues cibles à l’instance FindCriteria que vous fournissez à l’opération Find. Le code suivant montre comment découvrir des points de terminaison ICalculatorService correspondant à une étendue LDAP spécifique :
...
Créer DiscoveryClient
DiscoveryClient discoveryClient = new DiscoveryClient(« udpDiscoveryEndpoint »);
Rechercher les points de terminaison ICalculatorService dans l’étendue spécifiée
Étendue de l’URI = new URI(« ldap:///ou=engineering,o=exampleorg,c=us »);
FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));
findCriteria.Scopes.Add(scope);
FindResponse findResponse = discoveryClient.Find(findCriteria);
...
L’utilisation des étendues permet d’affiner votre implémentation de découverte afin que les clients puissent découvrir plus facilement les points de terminaison de service spécifiques qui les intéressent. La découverte permet également une personnalisation supplémentaire. Par exemple, les services peuvent ajouter des métadonnées XML personnalisées à un point de terminaison. Ces informations sont envoyées au client en réponse à la requête du client.
Annonces de service
WCF 4 facilite également la configuration des services pour « annoncer » leurs points de terminaison lorsqu’ils démarrent. Cela permet aux clients qui « écoutent » d’en savoir plus sur les nouveaux points de terminaison de service dès qu’ils rejoignent le réseau, réduisant ainsi la quantité de sondages (et de messages de multidiffusion) effectués par les clients.
Vous pouvez configurer un service avec un point de terminaison d’annonce à l’aide du <comportement serviceDiscovery> . Le <comportement serviceDiscovery> vous permet de définir une collection de points de terminaison d’annonce qui seront exposés par le service. Vous pouvez utiliser le standard « udpAnnouncementEndpoint » dans la plupart des cas.
Vous devez également configurer le service avec un « udpDiscoveryEndpoint » standard si vous souhaitez qu’il réponde aux sondes de découverte initiées par les clients. L’exemple suivant montre une configuration classique :
<configuration>
<system.serviceModel>
<services>
<service name="CalculatorService »>
<endpoint binding="wsHttpBinding » contract="ICalculatorService"/>
<endpoint kind="udpDiscoveryEndpoint"/>
</service>
</Services>
<behaviors>
<serviceBehaviors>
<Comportement>
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="udpAnnouncementEndpoint"/>
</announcementEndpoints>
</serviceDiscovery>
</Comportement>
</serviceBehaviors>
</Comportements>
</system.serviceModel>
</Configuration>
Une fois cette configuration en place, le service s’annonce lui-même lorsqu’il sera en ligne et il annoncera également quand il sera hors connexion. Pour tirer parti de ces annonces, vous devez spécifiquement concevoir vos clients pour les écouter au moment de l’exécution. Pour ce faire, hébergez un service d’annonce dans l’application cliente qui implémente le protocole d’annonce WS-Discovery.
WCF 4 est fourni avec une classe appelée AnnouncementService conçue spécifiquement à cet effet. AnnouncementService fournit deux gestionnaires d’événements : OnlineAnnouncementReceived et OfflineAnnouncementReceived. Les applications clientes peuvent simplement héberger un instance d’AnnouncementService à l’aide de ServiceHost et inscrire des gestionnaires d’événements pour ces deux événements.
Chaque fois qu’un service se met en ligne et s’annonce lui-même, le service d’annonce hébergé par le client reçoit l’annonce « en ligne » et OnlineAnnouncementReceived se déclenche dans le client. Lorsque le service est hors connexion, il envoie une annonce « hors connexion » et OfflineAnnouncementReceived se déclenche dans le client. Voici un exemple d’application cliente qui héberge AnnouncementService et implémente des gestionnaires pour les deux événements d’annonce :
client de classe
{
public static void Main()
{
Créer un instance AnnouncementService
AnnouncementService announcementService = new AnnouncementService();
S’abonner aux événements d’annonce
announcementService.OnlineAnnouncementReceived += OnOnlineEvent;
announcementService.OfflineAnnouncementReceived += OnOfflineEvent;
Créer ServiceHost pour AnnouncementService
using (ServiceHost announcementServiceHost =
new ServiceHost(announcementService))
{
Écoutez les annonces envoyées via la multidiffusion UDP
announcementServiceHost.AddServiceEndpoint(
new UdpAnnouncementEndpoint());
announcementServiceHost.Open();
Console.WriteLine(« Écoute des annonces de service. »);
Console.WriteLine();
Console.WriteLine(« Appuyez sur <ENTRÉE> pour se terminer. »);
Console.ReadLine();
}
}
static void OnOnlineEvent(object sender, AnnouncementEventArgs e)
{
Console.WriteLine();
Console.WriteLine(« Reçu une annonce en ligne de {0}: »,
e.EndpointDiscoveryMetadata.Address);
PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata) ;
}
static void OnOfflineEvent(object sender, AnnouncementEventArgs e)
{
Console.WriteLine();
Console.WriteLine(« Reçu une annonce hors connexion de {0}: »,
e.EndpointDiscoveryMetadata.Address);
PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata) ;
}
...
}
Figure 11 : Écoute des messages d’annonce de découverte
Supposons maintenant que j’exécute ce programme client et que je le laisse opérationnel pendant un certain temps. Ensuite, j’exécute quelques instances de l’application hôte de service. Lorsque chacun démarre, un message d’annonce « en ligne » s’affiche dans la fenêtre de la console cliente. Lorsque je ferme chacune des applications hôtes du service, un message d’annonce « hors connexion » s’affiche dans la fenêtre de la console cliente. La figure 11 montre la fenêtre de console cliente résultante après avoir effectué ce que je viens de décrire.
N’oubliez pas que le mode de découverte ad hoc fonctionne uniquement sur un sous-réseau local. Si vous souhaitez utiliser WS-Discovery au-delà des limites de votre réseau local, vous devez passer au mode de découverte managée. WCF 4 prend également en charge la génération des composants de découverte managés nécessaires.
Découverte de services managés
L’implémentation du mode de découverte managée est un peu plus impliquée que le mode ad hoc, car elle vous oblige à implémenter un service proxy de découverte. Le service proxy de découverte est le composant qui effectue le suivi de tous les points de terminaison de service disponibles. Dans cet exemple, nous utilisons la fonctionnalité d’annonce pour mettre à jour le proxy de découverte. Il existe de nombreuses autres façons de fournir à un proxy de découverte les informations de découverte pertinentes, par exemple, vous pouvez connecter une base de données existante de points de terminaison et capturer des données à partir de là. Comment implémenter un service proxy de découverte ?
WCF 4 est fourni avec une classe de base nommée DiscoveryProxy à partir de laquelle vous pouvez dériver pour implémenter un service proxy de découverte. La figure 12 montre le début d’une implémentation de service proxy de découverte personnalisée. Les exemples du KIT de développement logiciel (SDK) .NET 4 contiennent un exemple d’implémentation complet à titre de référence. Une fois que vous avez terminé d’implémenter le service proxy de découverte, vous devez l’héberger quelque part.
Figure 12 : Implémentation d’un service proxy de découverte personnalisé
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
classe publique MyDiscoveryProxy : DiscoveryProxyBase
{
Dépôt pour stocker EndpointDiscoveryMetadata.
Une base de données ou un fichier plat peut également être utilisé à la place.
Dictionary<EndpointAddress, EndpointDiscoveryMetadata> onlineServices;
public MyDiscoveryProxy()
{
this.onlineServices =
new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();
}
OnBeginOnlineAnnouncement est appelé lorsqu’un message Hello est reçu par proxy
protected override IAsyncResult OnBeginOnlineAnnouncement(
DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata
endpointDiscoveryMetadata, rappel AsyncCallback, état de l’objet)
{
Ce. AddOnlineService(endpointDiscoveryMetadata) ;
retourne le nouveau OnOnlineAnnouncementAsyncResult(callback, state) ;
}
protected override void OnEndOnlineAnnouncement(IAsyncResult result)
{
OnOnlineAnnouncementAsyncResult.End(result) ;
}
OnBeginOfflineAnnouncement est appelé lorsqu’un message Bye est reçu par proxy
protected override IAsyncResult OnBeginOfflineAnnouncement(
DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata
endpointDiscoveryMetadata, rappel AsyncCallback, état de l’objet)
{
Ce. RemoveOnlineService(endpointDiscoveryMetadata) ;
retourne le nouveau OnOfflineAnnouncementAsyncResult(callback, state) ;
}
protected override void OnEndOfflineAnnouncement(IAsyncResult result)
{
OnOfflineAnnouncementAsyncResult.End(result) ;
}
OnBeginFind est appelé lorsqu’un message de demande de sonde est reçu par le proxy
protected override IAsyncResult OnBeginFind(
FindRequestContext findRequestContext, rappel AsyncCallback, état de l’objet)
{
Ce. MatchFromOnlineService(findRequestContext) ;
retourne le nouveau OnFindAsyncResult(callback, state) ;
}
protected override void OnEndFind(IAsyncResult result)
{
OnFindAsyncResult.End(result) ;
}
...
Pour cet exemple, je vais simplement héberger le service MyDiscoveryProxy dans une application console. Je vais configurer l’hôte avec deux points de terminaison : un point de terminaison de découverte et un point de terminaison d’annonce. L’exemple suivant montre comment héberger correctement le service MyDiscoveryProxy avec ces deux points de terminaison :
programme de classe
{
public static void Main()
{
Uri probeEndpointAddress = new Uri(« net.tcp://localhost:8001/Probe »);
Uri announcementEndpointAddress =
new Uri(« net.tcp://localhost:9021/Announcement »);
ServiceHost proxyServiceHost = new ServiceHost(new MyDiscoveryProxy());
DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(
new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));
discoveryEndpoint.IsSystemEndpoint = false ;
AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(
new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));
proxyServiceHost.AddServiceEndpoint(discoveryEndpoint) ;
proxyServiceHost.AddServiceEndpoint(announcementEndpoint) ;
proxyServiceHost.Open();
Console.WriteLine(« Proxy Service started. »);
Console.WriteLine();
Console.WriteLine(« Appuyez sur <ENTRÉE> pour arrêter le service. »);
Console.WriteLine();
Console.ReadLine();
proxyServiceHost.Close();
}
}
Une fois que vous avez un service proxy de découverte opérationnel, vous pouvez configurer vos services pour qu’ils s’annoncent directement au service proxy de découverte. De même, vous pouvez configurer vos applications clientes pour sonder directement le service proxy de découverte (plus de messagerie multidiffusion).
Vous configurez le service pour qu’il s’annonce directement auprès du service proxy de découverte en spécifiant l’adresse d’annonce du proxy de découverte lors de la création du point de terminaison d’annonce dans l’application hôte du service. L’exemple suivant montre comment procéder :
...
Uri baseAddress = new Uri(« net.tcp://localhost:9002/CalculatorService/ » +
Guid.NewGuid(). ToString());
Uri announcementEndpointAddress = new Uri(« net.tcp://localhost:9021/Announcement »);
ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService), baseAddress);
ServiceEndpoint netTcpEndpoint = serviceHost.AddServiceEndpoint(
typeof(ICalculatorService), new NetTcpBinding(), string. Vide);
Créer un point de terminaison d’annonce pointant vers le service proxy hébergé
AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(
new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));
ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior();
serviceDiscoveryBehavior.AnnouncementEndpoints.Add(announcementEndpoint) ;
serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);
serviceHost.Open();
...
Vous pouvez ensuite configurer vos applications clientes pour communiquer directement avec le service proxy de découverte en spécifiant l’adresse de sonde du proxy de découverte lors de la création du DiscoveryEndpoint dans l’application cliente. L’exemple suivant illustre une façon de procéder :
...
Créez un point de terminaison de découverte qui pointe vers le service proxy.
Uri probeEndpointAddress = new Uri(« net.tcp://localhost:8001/Probe »);
DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(
new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));
Créer DiscoveryClient à l’aide du discoveryEndpoint créé précédemment
DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint) ;
Rechercher des points de terminaison ICalculatorService
FindResponse findResponse = discoveryClient.Find(
new FindCriteria(typeof(ICalculatorService)));
...
Passons maintenant en revue un exemple complet. Tout d’abord, j’exécute l’application proxy de découverte afin que le service proxy de découverte soit disponible. Ensuite, j’exécuterai une instance de l’application hôte du service, qui s’annoncera elle-même avec le proxy de découverte. Dans ce cas, un message s’affiche dans la fenêtre de console de l’application proxy de découverte (voir figure 13). Cela montre que le service s’est annoncé avec succès auprès du proxy de découverte, et que le proxy de découverte a enregistré les informations sur le nouveau point de terminaison de service « en ligne ». À présent, si nous exécutons le code client indiqué ci-dessus, il sonde directement le proxy de découverte et récupère l’adresse du point de terminaison du service cible en cours d’exécution.
Figure 13 : Sortie du service proxy de découverte au moment de l’exécution
La beauté de la découverte de services managés est qu’elle fonctionne au-delà des limites du réseau (elle est basée sur les appels de service traditionnels) et qu’elle réduit le besoin de messagerie multidiffusion au sein de votre solution de découverte. En outre, étant donné que les clients passent par un proxy de découverte pour rechercher des services, les services eux-mêmes n’ont pas besoin d’être opérationnels tout le temps pour être découverts.
Utilisation du proxy de découverte avancée
Le modèle de programmation WCF vous offre une grande flexibilité dans l’implémentation d’un proxy de découverte. La réception d’annonces est un moyen de remplir votre liste de services ; toutefois, il ne s’agit pas de la seule méthode. Par exemple, si votre environnement contient déjà un dépôt de service, vous pouvez facilement créer une façade de proxy de découverte au-dessus de ce magasin afin de rendre le dépôt détectable au moment de l’exécution.
Un proxy de découverte peut être configuré en mode ad hoc ou gestion. Lorsqu’ils fonctionnent en mode managé, les clients communiquent directement avec le proxy de manière unicast à l’aide d’annonces, de sondes et de résolutions. Le proxy transmet également la réponse de manière unicast à l’expéditeur.
Si vous fonctionnez en mode ad hoc, un proxy peut écouter les messages de découverte de multidiffusion et répondre directement à l’expéditeur. Dans ce mode ad hoc, un proxy peut également être configuré spécifiquement pour supprimer les messages de multidiffusion. Autrement dit, si un proxy reçoit un message de multidiffusion, il informe l’expéditeur de sa présence et l’informe de diriger d’autres requêtes sur le proxy, évitant ainsi d’autres messages de multidiffusion.
Pour plus d’informations sur ces scénarios de découverte avancée, consultez l'WS-Discovery Primer à l’adresse http://www.oasis-open.org/committees/download.php/32184/WS-D-primer-wd-04.docx.
Service de routage
Dans certains environnements orientés service, il est souvent utile de tirer parti des services de « routage » centralisés qui agissent en tant que courtiers ou passerelles vers les services métier réels dispersés dans le organization. Cela dissocie les consommateurs des services d’entreprise réels et permet d’effectuer différents types de traitement intermédiaire au sein du nœud de routage.
Par exemple, certains environnements utilisent le routage pour implémenter une limite de sécurité centralisée que tous les messages entrants doivent passer. Certains utilisent des techniques de routage basées sur le contenu pour déterminer le service cible à utiliser en fonction du contenu d’un message entrant particulier. D’autres utilisent le routage pour implémenter le pontage de protocole, ce qui permet aux consommateurs d’utiliser un ensemble de protocoles pour communiquer tandis que le routeur utilise un autre ensemble de protocoles pour communiquer avec le service cible. Il n’est également pas rare d’utiliser le routage pour diverses techniques d’équilibrage de charge ou même de contrôle de version de service.
Quelle que soit la raison, le modèle de « routage intermédiaire » est une exigence courante lors de la création de solutions SOA à grande échelle aujourd’hui. Dans WCF 3.x, il n’y avait pas de prise en charge officielle du routage. Bien que le framework ait fourni les API nécessaires pour implémenter vos propres services de routage, il a fallu beaucoup de travail pour le faire correctement. Plusieurs articles ont été publiés dans MSDN Magazine montrant comment y parvenir.
Étant donné que le routage est une exigence courante de nos jours, WCF 4 est désormais fourni avec un « service de routage » officiel dans l’infrastructure que vous pouvez simplement héberger et configurer dans vos propres solutions.
Présentation du service de routage
WCF 4 est fourni avec une nouvelle classe appelée RoutingService, qui fournit une implémentation de routage WCF générique à utiliser dans vos applications. RoutingService peut gérer le routage des messages sur n’importe quel protocole pris en charge par WCF à l’aide d’une variété de modèles de messagerie différents tels que la messagerie unidirectionnelle, la requête-réponse et la messagerie duplex. Voici la définition de la classe RoutingService :
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any,
InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false, ValidateMustUnderstand = false),
AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
Classe scellée publique RoutingService : // les contrats autorisent différents modèles de communication
ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter,
IDuplexSessionRouter, IDisposable
{
... // implémentation omise
}
Comme vous pouvez le voir, la classe RoutingService dérive de plusieurs contrats de service afin de prendre en charge plusieurs modèles de messagerie. Chaque contrat de service fournit la prise en charge d’un modèle de messagerie différent, y compris la prise en charge des communications basées sur les sessions, le cas échéant.
L’objectif de RoutingService est de recevoir les messages entrants des consommateurs et de les « router » vers un service en aval approprié. RouterService détermine le service cible à utiliser en évaluant chaque message entrant par rapport à un ensemble de filtres de messages. Par conséquent, en tant que développeur, vous contrôlez le comportement de routage en définissant les filtres de messages, généralement dans un fichier de configuration. Les services cibles peuvent résider sur la même machine que RouterService, mais ils ne sont pas obligés de le faire : ils peuvent également être distribués sur le réseau et ils peuvent nécessiter différents protocoles.
Hébergement du service de routage
Vous pouvez héberger routingService dans votre application comme vous le feriez pour n’importe quel autre service WCF. Vous créez simplement une instance ServiceHost et spécifiez RoutingService pour le type de service. Une fois que vous appelez Open sur le instance ServiceHost, votre service routage est prêt à « acheminer » les messages, comme illustré ici :
using System;
à l’aide de System.ServiceModel ;
à l’aide de System.ServiceModel.Routing ;
public static void Main()
{
Créez un ServiceHost pour le type RoutingService.
using (ServiceHost serviceHost =
new ServiceHost(typeof(RoutingService)))
{
Essayer
{
serviceHost.Open();
Console.WriteLine(« Le service de routage est en cours d’exécution. »);
Console.WriteLine(« Appuyez <sur Entrée> pour arrêter le routeur. »);
Le service est désormais accessible.
Console.ReadLine();
serviceHost.Close();
}
catch (CommunicationException)
{
serviceHost.Abort();
}
}
}
Vous configurez également RoutingService comme n’importe quel autre service et c’est là que vous définissez les filtres de routage. Tout d’abord, vous devez le configurer avec un ou plusieurs points de terminaison. Lorsque vous définissez un point de terminaison de routage, vous choisissez une liaison WCF et l’un des contrats de service de routage implémentés par RoutingService indiqué ci-dessus (par exemple, ISimplexDatagramRouter, IRequestReplyRouter, etc.). Vous pouvez exposer plusieurs points de terminaison sur votre service de routage si vous souhaitez prendre en charge plusieurs modèles de messagerie ou liaisons WCF.
L’exemple suivant montre comment configurer RoutingService avec quatre points de terminaison de routage : deux qui utilisent basicHttpBinding (unidirectionnel et requête-réponse) et deux qui utilisent WSHttpBinding (unidirectionnel et requête-réponse). Notez que cela ressemble à la configuration d’un autre service WCF :
<configuration>
<system.serviceModel>
<services>
<--ROUTING SERVICE -->
<comportement du serviceConfiguration="routingData »
name="System.ServiceModel.Routing.RoutingService »>
<host>
<baseAddresses>
<add baseAddress= »https://localhost:8000/routingservice/router"/>
</baseAddresses>
</Hôte>
<!--
Définissez et configurez le point de terminaison sur lequel nous voulons que le routeur écoute et le
Contrat que nous voulons utiliser. Les contrats fournis par le routeur sont les suivants :
ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter et
IDuplexSessionRouter.
-->
<endpoint address="oneway-basic »
binding="basicHttpBinding »
name="onewayEndpointBasic »
contract="System.ServiceModel.Routing.ISimplexDatagramRouter » />
<endpoint address="oneway-ws »
binding="wsHttpBinding »
name="onewayEndpointWS »
contract="System.ServiceModel.Routing.ISimplexDatagramRouter » />
<endpoint address="twoway-basic »
binding="basicHttpBinding »
name="reqReplyEndpointBasic »
contract="System.ServiceModel.Routing.IRequestReplyRouter » />
<endpoint address="twoway-ws »
binding="wsHttpBinding »
name="reqReplyEndpointWS »
contract="System.ServiceModel.Routing.IRequestReplyRouter » />
</service>
</Services>
...
Les interfaces ISimplexDatagramRouter et IRequestReplyRouter définissent des définitions génériques de contrat de service unidirectionnel et de demande-réponse qui peuvent être utilisées conjointement avec des contrats de service spécifiques à l’entreprise. Voici comment ces interfaces ont été définies dans WCF :
[ServiceContract(Namespace= »https://schemas.microsoft.com/netfx/2009/05/routing",
SessionMode = SessionMode.Allowed)]
interface publique ISimplexDatagramRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = « * »)]
IAsyncResult BeginProcessMessage(Message message, rappel AsyncCallback,
état de l’objet);
void EndProcessMessage(IAsyncResult result) ;
}
[ServiceContract(Namespace= »https://schemas.microsoft.com/netfx/2009/05/routing",
SessionMode = SessionMode.Allowed)]
interface publique IRequestReplyRouter
{
[OperationContract(AsyncPattern = true, IsOneWay= false, Action = « * »,
ReplyAction = « * »)]
[GenericTransactionFlow(TransactionFlowOption.Allowed)]
IAsyncResult BeginProcessRequest(Message message, rappel AsyncCallback,
état de l’objet);
Message EndProcessRequest(IAsyncResult result) ;
}
La configuration de point de terminaison ci-dessus expose les points de terminaison de routage que les consommateurs peuvent utiliser. Les applications clientes choisissent l’un de ces points de terminaison à utiliser dans leur code client et dirigent tous les appels de service directement vers RoutingService. Lorsque RoutingService reçoit un message via l’un de ces points de terminaison, il évalue les filtres de message de routage pour déterminer où transférer le message.
Configuration de RoutingService avec des filtres de messages
Vous pouvez configurer RoutingService avec des filtres de message par le biais du code ou de la configuration (comme tout le reste dans WCF). WCF 4 fournit un RoutingBehavior pour gérer les filtres de message de routage. Vous devez d’abord activer RoutingBehavior sur RouterService, puis spécifier le nom de la table de filtres que vous souhaitez utiliser avec cette instance particulière du service de routage :
<configuration>
<system.serviceModel>
...
<behaviors>
<serviceBehaviors>
<behavior name="routingData »>
<serviceMetadata httpGetEnabled="True"/>
<-- Définir le comportement de routage et spécifier le nom de la table de filtres ->
<routing filterTableName="filterTable1 » />
</Comportement>
</serviceBehaviors>
</Comportements>
...
Si vous regardez dans l’exemple précédent où nous avons configuré RoutingService avec des points de terminaison, vous verrez que nous avons appliqué le comportement « routingData » au service via l’attribut behaviorConfiguration. Ensuite, nous devons définir une table de filtres nommée « filterTable1 ».
Toutefois, avant de pouvoir définir une table de filtres, nous avons besoin de définitions de point de terminaison pour les services cibles vers lesquels nous avons l’intention d’acheminer. Vous définissez ces points de terminaison cibles dans la section configuration du client> WCF<, car RoutingService est essentiellement le « client » lorsqu’il transfère des messages à un service cible. L’exemple suivant montre comment définir deux points de terminaison cibles que nous pouvons acheminer :
<configuration>
...
<-- Définir les points de terminaison clients avec lesquels le routeur doit communiquer.
Il s’agit des destinations auxquelles le routeur envoie des messages. -->
<client>
<endpoint name="CalculatorService1 »
address= »https://localhost:8000/servicemodelsamples/calcservice1"
binding="wsHttpBinding » contract="* » />
<endpoint name="CalculatorService2 »
address= »https://localhost:8001/servicemodelsamples/calcservice2"
binding="wsHttpBinding » contract="* » />
</Client>
...
Nous pouvons maintenant définir la table de filtres réelle, qui déterminera la logique de routage au moment de l’exécution. Vous définissez les entrées de table de filtre dans l’élément <filterTables> . Chaque entrée dans le <filterTable> définit un mappage entre un « filtre » de routage et un point de terminaison cible. Vous définissez les « filtres » que vous souhaitez utiliser dans l’élément <filters> : chaque <entrée de filtre> spécifie le type de filtre que vous souhaitez utiliser avec les données spécifiques au filtre (comme une valeur d’action, une expression XPath, etc.).
L’exemple suivant montre comment configurer une table de filtres avec un seul filtre mappé au point de terminaison CalculatorService1. Dans ce cas, le filtre « MatchAll » correspond à tous les messages entrants :
<configuration>
...
<--ROUTING SECTION -->
<Routage>
<-- Définir les filtres que nous voulons que le routeur utilise. -->
<filters>
<filter name="MatchAllFilter1 » filterType="MatchAll » />
</Filtres>
<-- Définir la table de filtres qui contient le filtre matchAll -->
<filterTables>
<filterTable name="filterTable1 »>
<-- Mapper le filtre à un point de terminaison client précédemment défini.
Les messages correspondant à ce filtre sont envoyés à cette destination. -->
<add filterName="MatchAllFilter1 » endpointName="CalculatorService1 » />
</filterTable>
</filterTables>
</Routage>
</system.serviceModel>
</Configuration>
Nous pouvons vérifier que le routage fonctionne correctement en exécutant l’application hôte du service de routage, l’application hôte CalculatorService1 et un client conçu pour envoyer des messages à l’un des points de terminaison du routeur. Lorsque nous exécutons le client, nous devons voir les messages arriver dans CalculatorService 1 après qu’ils ont été « routés » par le service de routage intermédiaire (voir la figure 14, la figure 15 et la figure 16).
Figure 14 : Application hôte RoutingService
Figure 15 : Client ciblant routingService à l’emplacement https://localhost:8000/routingservice/router
Figure 16 : Service cible (CalculatorService1)
Filtres de messages et routage basé sur le contenu
WCF est fourni avec plusieurs classes MessageFilter intégrées que vous pouvez utiliser conjointement avec vos filtres de messages de routage pour inspecter le contenu des messages entrants.
Par exemple, WCF fournit un ActionMessageFilter qui vous permet de faire correspondre des valeurs d’action WS-Addressing spécifiques. WCF fournit également EndpointAddressMessageFilter, EndpointNameMessageFilter et PrefixEndpointAddressMessageFilter qui vous permettent de faire correspondre des détails de point de terminaison spécifiques. L’un des plus flexibles est le XPathMessageFilter, qui vous permet d’évaluer les expressions XPath par rapport aux messages entrants. Tous ces filtres vous permettent d’effectuer un routage basé sur le contenu au sein de votre solution.
En plus de ces types MessageFilter intégrés, WCF 4 permet également de définir des filtres de messages personnalisés, qui sont l’un des principaux points d’extensibilité pour RoutingService.
Examinons un exemple d’exécution d’un routage basé sur le contenu basé sur des valeurs d’action. Supposons que nous souhaitions acheminer la moitié des opérations CalculatorService vers CalculatorService1 et l’autre moitié vers CalculatorService2. Pour ce faire, définissez des filtres pour chacune des différentes valeurs d’action CalculatorService et mappez la moitié d’entre elles à chaque point de terminaison de service cible, comme illustré ici :
<configuration>
...
<--ROUTING SECTION -->
<Routage>
<-- Définir les filtres que nous voulons que le routeur utilise. -->
<filters>
<filter name="addFilter » filterType="Action »
filterData= »http://Microsoft.Samples.ServiceModel/ICalculator/Add"/>
<filter name="subFilter » filterType="Action »
filterData= »http://Microsoft.Samples.ServiceModel/ICalculator/Subtract"/>
<filter name="mulFilter » filterType="Action »
filterData= »http://Microsoft.Samples.ServiceModel/ICalculator/Multiply"/>
<filter name="divFilter » filterType="Action »
filterData= »http://Microsoft.Samples.ServiceModel/ICalculator/Divide"/>
</Filtres>
<filterTables>
<filterTable name="filterTable1 »>
<add filterName="addFilter » endpointName="CalculatorService1"/>
<add filterName="subFilter » endpointName="CalculatorService2"/>
<add filterName="mulFilter » endpointName="CalculatorService1"/>
<add filterName="divFilter » endpointName="CalculatorService2"/>
</filterTable>
</filterTables>
</Routage>
</system.serviceModel>
</Configuration>
Maintenant, lorsque nous exécutons la solution et le client qui appelle les quatre opérations, la moitié des opérations s’affiche dans chaque fenêtre de console de service (voir la figure 17 et la figure 18).
Figure 17 : Sortie de CalculatorService1
Figure 18 : Sortie de CalculatorService2
XPathMessageFilter vous offre encore plus de flexibilité, car vous pouvez fournir une variété d’expressions XPath différentes à évaluer par rapport aux messages entrants. Vos expressions XPath peuvent évaluer n’importe quelle partie du message entrant, y compris les en-têtes SOAP ou le corps SOAP. Cela vous offre une grande flexibilité lors de la création de filtres de messages basés sur le contenu. Pour comprendre les mécanismes de XPathMessageFilter, voici comment réécrire le dernier exemple à l’aide d’expressions XPath :
<configuration>
...
<--ROUTING SECTION -->
<Routage>
<-- Définir les filtres que nous voulons que le routeur utilise. -->
<filters>
<filter name="addFilter » filterType="XPath »
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Add'"/>
<filter name="subFilter » filterType="XPath »
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Subtract'"/>
<filter name="mulFilter » filterType="XPath »
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Multiply'"/>
<filter name="divFilter » filterType="XPath »
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Divide'"/>
</Filtres>
<namespaceTable>
<add prefix="s » namespace= »http://www.w3.org/2003/05/soap-envelope" />
<add prefix="wsa » namespace= »http://www.w3.org/2005/08/addressing" />
</namespaceTable>
<filterTables>
<filterTable name="filterTable1 »>
<add filterName="addFilter » endpointName="CalculatorService1"/>
<add filterName="subFilter » endpointName="CalculatorService2"/>
<add filterName="mulFilter » endpointName="CalculatorService1"/>
<add filterName="divFilter » endpointName="CalculatorService2"/>
</filterTable>
</filterTables>
</Routage>
</system.serviceModel>
</Configuration>
Notez que l’attribut filterData contient une expression XPath qui sera évaluée par rapport au message entrant (les expressions case activée simplement les valeurs d’action dans cet exemple). Notez que j’ai également défini un ensemble de liaisons de préfixes d’espace de noms à l’aide de l’élément <namespaceTable> . Cela est nécessaire si vous souhaitez utiliser des préfixes d’espace de noms dans vos expressions XPath, comme je l’ai fait ci-dessus. La ré-exécution de la solution avec cette configuration produit les mêmes résultats qu’auparavant (voir les figure 17 et 18).
Vous devez utiliser cette technique de filtre XPath chaque fois que vous devez acheminer des messages en fonction d’en-têtes SOAP personnalisés ou en fonction du contenu trouvé dans le corps du message SOAP.
Pontage de protocoles
Dans les exemples précédents, nous utilisions la même liaison WCF (WSHttpBinding) pour communiquer entre le client et le routeur, et entre le routeur et les services cibles. RoutingService est capable de ponter la communication entre la plupart des liaisons WCF. Par exemple, vous pouvez configurer le routeur pour que les clients communiquent avec lui via WSHttpBinding, mais le routeur communique ensuite avec les services cibles en aval à l’aide de NetTcpBinding ou netNamedPipeBinding.
Voyons comment configurer RoutingService pour gérer ce scénario. Nous allons laisser la configuration du point de terminaison RoutingService identique à celle ci-dessus, ce qui permet aux consommateurs de communiquer avec RoutingService via BasicHttpBinding ou WSHttpBinding. Mais maintenant, nous allons modifier les définitions de point de terminaison client pour les services cibles afin d’utiliser NetTcpBinding et NetNamedPipeBinding, comme indiqué ici :
<configuration>
...
<-- Définir les points de terminaison clients avec lesquels le routeur doit communiquer.
Il s’agit des destinations auxquelles le routeur envoie des messages. -->
<client>
<endpoint name="CalculatorService1 »
address="net.tcp://localhost:8001/servicemodelsamples/calcservice1 »
binding="netTcpBinding » contract="* » />
<endpoint name="CalculatorService2 »
address="net.pipe://localhost/servicemodelsamples/calcservice2 »
binding="netNamedPipeBinding » contract="* » />
</Client>
...
Et, bien sûr, nous devrons mettre à jour les applications CalculatorService1 et CalculatorService2 pour prendre en charge les points de terminaison NetTcpBinding et NetNamedPipeBinding compatibles, respectivement. Une fois cette configuration en place, les consommateurs peuvent communiquer avec RoutingService à l’aide de BasicHttpBinding/WSHttpBinding et le routeur communiquera avec les services cibles à l’aide de NetTcpBinding ou de NetNamedPipeBinding en fonction du service vers lequel le message est acheminé.
Remise d’erreurs et tolérance de panne
RoutingService fournit également un mécanisme intégré pour traiter les erreurs de communication d’exécution et prendre en charge un niveau de tolérance de panne de base. Lorsque vous définissez votre table de filtres, vous pouvez définir différentes listes de points de terminaison alternatifs qui seront utilisés par RoutingService si la communication avec le point de terminaison cible initial génère une erreur. Cela permet essentiellement d’avoir des listes de points de terminaison de « sauvegarde ».
L’exemple suivant montre comment définir une liste de points de terminaison de sauvegarde dans l’élément <backupLists> que nous pouvons associer à nos entrées de table de filtres :
<configuration>
...
<--ROUTING SECTION -->
<Routage>
... <! -- Définissez les filtres que nous voulons que le routeur utilise. -->
<filterTables>
<filterTable name="filterTable1 »>
<add filterName="addFilter » endpointName="CalculatorService1 »
alternateEndpoints="backupEndpoints"/>
<add filterName="subFilter » endpointName="CalculatorService1 »
alternateEndpoints="backupEndpoints"/>
<add filterName="mulFilter » endpointName="CalculatorService1 »
alternateEndpoints="backupEndpoints"/>
<add filterName="divFilter » endpointName="CalculatorService1 »
alternateEndpoints="backupEndpoints"/>
</filterTable>
</filterTables>
<backupLists>
<backupList name="backupEndpoints »>
<add endpointName="CalculatorService2"/>
</backupList>
</backupLists>
</Routage>
</system.serviceModel>
</Configuration>
Notez comment nous avons configuré toutes les entrées de table de filtre pour les transférer à CalculatorService1 dans cet exemple, car CalculatorService2 est désormais notre point de terminaison de « sauvegarde » qui ne sera utilisé que lorsque CalculatorService1 génère une TimeoutException, une CommunicationException ou un type d’exception dérivé. Par exemple, si j’exécute à nouveau la solution et que je ferme CalculatorService1, puis que j’exécute le programme client, tous les messages se retrouvent dans CalculatorService2. Il est important de noter, une fois de plus, que toute cette configuration de routage peut être effectuée dynamiquement dans le code de l’application hôte.
Comportement de routage de multidiffusion
RoutingService prend également en charge le routage automatique d’un message entrant particulier vers plusieurs destinations de manière « multidiffusion ». Lorsque le message entrant correspond à plusieurs filtres trouvés dans la table de filtres configurée, RoutingService route automatiquement le message vers chacun des points de terminaison cibles associés aux filtres « mis en correspondance ».
L’exemple suivant montre deux entrées de routage configurées avec le même filtre générique, qui correspond à tous les messages entrants :
<configuration>
...
<--ROUTING SECTION -->
<Routage>
<-- Définir les filtres que nous voulons que le routeur utilise. -->
<filters>
<filter name="wildcardFilter » filterType="MatchAll » />
</Filtres>
<filterTables>
<filterTable name="filterTable1 »>
<add filterName="wildcardFilter » endpointName="CalculatorService1"/>
<add filterName="wildcardFilter » endpointName="CalculatorService2"/>
<add filterName="wildcardFilter » endpointName="CalculatorService3"/>
</filterTable>
</filterTables>
</Routage>
</system.serviceModel>
</Configuration>
Une fois cette configuration en place, chaque message SOAP entrant est automatiquement routé vers tous les points de terminaison cibles, quel que soit ce qui se trouve dans les messages.
Ce comportement de multidiffusion se compose avec les fonctionnalités de pontage de protocole et de gestion des erreurs décrites dans les sections précédentes. Le seul problème est que la multidiffusion fonctionne uniquement pour la communication unidirectionnelle ou duplex, mais pas pour la communication demande-réponse, car le système sous-jacent doit maintenir un rapport un-à-un entre les demandes sortantes et les réponses entrantes.
Prise en charge DE REST améliorée
WCF 4 est fourni avec plusieurs nouvelles fonctionnalités qui s’avèrent utiles lors de la création de services RESTful à l’aide de WCF. Cet ensemble de fonctionnalités est maintenant appelé Services WebHttp WCF. Ils incluent la prise en charge d’une page d’aide automatique qui décrit le service RESTful aux consommateurs, la mise en cache HTTP simplifiée, la sélection du format de message, les exceptions rest-friendly, l’intégration de routage ASP.NET, certains nouveaux modèles de projet Visual Studio, etc. Nous n’aurons pas d’espace pour couvrir toutes ces fonctionnalités ici en détail, mais je vais vous donner une introduction rapide à quelques-uns de mes favoris ci-dessous, ainsi que des liens vers plus d’informations sur le reste.
Bon nombre de ces fonctionnalités ont été introduites l’année dernière par le Kit de démarrage REST WCF et sont maintenant intégrées au framework officiel. Vous verrez peut-être d’autres fonctionnalités du Kit de démarrage REST WCF suivre à l’avenir.
Page d’aide automatique
Lors de l’utilisation de la classe WebServiceHost dans WCF 4, vos services RESTful bénéficient automatiquement des avantages de la fonctionnalité de page d’aide automatique. Il s’agit d’un ajout très nécessaire lors de l’utilisation de REST en raison de l’absence de métadonnées WSDL et de la génération de code côté client . Il est donc beaucoup plus facile pour les consommateurs de déterminer comment commencer à utiliser votre service. Il est donc généralement judicieux d’activer cette nouvelle fonctionnalité.
Lorsque vous utilisez la classe WebServiceHost pour héberger votre service, elle configure automatiquement votre service avec le WebHttpBehavior et ajoute un point de terminaison HTTP par défaut configuré avec WebHttpBinding (à l’adresse HTTP de base). À compter de WCF 4, la classe WebHttpBehavior est fournie avec une propriété HelpEnabled qui contrôle si la nouvelle page d’aide est activée ou non dans l’hôte. L’exemple de configuration suivant montre comment activer la fonctionnalité de page d’aide automatique pour un point de terminaison REST spécifique :
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true » />
<behaviors>
<endpointBehaviors>
<behavior name="HelpBehavior »>
<webHttp helpEnabled="true » />
</Comportement>
</endpointBehaviors>
</Comportements>
<services>
<service name="CounterResource »>
<endpoint behaviorConfiguration="HelpBehavior »
binding="webHttpBinding »
contract="CounterResource » />
</service>
</Services>
</system.serviceModel>
</Configuration>
Vous pouvez voir la page d’aide en accédant à l’adresse de base du service avec l'« aide » ajoutée à la fin de l’URL (voir la figure 19).
Figure 19 : Page d’aide automatique pour les services RESTFul
La page d’aide fournit une description lisible de chaque opération annotée avec [WebGet] ou [WebInvoke], et pour chacune d’elles, elle décrit le modèle d’URI, l’opération HTTP prise en charge et les formats de message pris en charge, essentiellement tout ce qu’un consommateur doit savoir (voir la figure 20). Vous pouvez également fournir une description plus conviviale en appliquant un attribut [Description] à chaque opération.
Pour chaque demande/réponse, la page d’aide fournit également un schéma XML et un exemple d’instance XML correspondant que les consommateurs peuvent utiliser pour s’intégrer au service. Les consommateurs peuvent utiliser le schéma pour générer des types sérialisables appropriés côté client ou inspecter simplement l’exemple de document XML pour déterminer manuellement comment écrire le code de traitement XML approprié. Les deux approches sont utiles.
Figure 20 : Page d’aide automatique pour une opération spécifique
Cette nouvelle fonctionnalité de page d’aide rend automatiquement vos services RESTful plus détectables, ce qui les rend finalement plus faciles à utiliser par d’autres utilisateurs. Vos consommateurs peuvent découvrir la conception de l’URI du service, les opérations HTTP prises en charge et les formats de requête/réponse, et votre description restera toujours synchronisée avec votre code WCF, de la même façon que les choses fonctionnent avec ASP.NET services Web.
Prise en charge de la mise en cache HTTP
L’un des principaux avantages potentiels de REST est la mise en cache HTTP. Toutefois, pour bénéficier de cet avantage, vous devez tirer parti des différents en-têtes de mise en cache HTTP dans vos messages de demande et de réponse. Vous pouvez effectuer cette opération dans vos opérations de service WCF en accédant manuellement aux en-têtes de demande/réponse via le instance WebOperationContext, mais il n’est pas trivial de le faire correctement.
Par conséquent, WCF 4 est fourni avec un modèle plus simple pour contrôler la mise en cache via l’attribut [AspNetCacheProfile] que vous pouvez appliquer de manière déclarative à vos opérations GET. Cet attribut vous permet de spécifier un nom de profil de mise en cache ASP.NET pour chaque opération et, en arrière-plan, il existe un inspecteur de mise en cache (CachingParameterInspector) qui gère tous les détails de la mise en cache HTTP sous-jacente.
L’implémentation [AspNetCacheProfile] s’appuie sur le mécanisme de mise en cache de sortie ASP.NET standard. L’exemple suivant montre comment appliquer l’attribut [AspNetCacheProfile] à une opération [WebGet] :
...
[AspNetCacheProfile(« CacheFor60Seconds »)]
[WebGet(UriTemplate=XmlItemTemplate)]
[OperationContract]
public Counter GetItemInXml()
{
return HandleGet();
}
...
Une fois ce paramètre en place, vous devez définir un profil de mise en cache de sortie ASP.NET nommé « CacheFor60Seconds » dans votre fichier web.config. La web.config suivante montre comment procéder :
<configuration>
<system.web>
<Cache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheFor60Seconds » duration="60 » varyByParam="format » />
</outputCacheProfiles>
</outputCacheSettings>
</Cache>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true » />
</system.serviceModel>
</Configuration>
Notez que le profil de mise en cache ASP.NET est défini pour mettre en cache la sortie pendant 60 secondes et qu’il est configuré pour faire varier le cache par la variable de chaîne de requête « format ». Cela est important, car le service en question prend en charge xml et JSON, que vous contrôlez via la variable de format (par exemple, « ?format=json »). Le service doit mettre en cache les réponses XML et JSON indépendamment les unes des autres.
Ce fichier de configuration montre également comment activer le mode de compatibilité ASP.NET, qui est nécessaire lorsque vous souhaitez tirer parti de diverses fonctionnalités ASP.NET telles que la mise en cache de sortie.
L’attribut [AspNetCacheProfile] facilite considérablement la mise en cache HTTP sans avoir à utiliser directement les en-têtes de mise en cache HTTP. Le comportement WCF sous-jacent s’occupe d’injecter les en-têtes HTTP Cache-Control, Date, Expires et Vary dans la réponse, dont les clients peuvent également tirer parti pour déterminer la sémantique de mise en cache appropriée autour des prochains allers-retours.
Sélection du format de message
Si vous examinez la figure 19, vous remarquerez que le service CounterResource prend en charge les formats XML et JSON pour les opérations GET, POST et PUT. Cela a été possible depuis WCF 3.5 via les propriétés RequestFormat et ResponseFormat trouvées sur les attributs [WebGet] et [WebInvoke].
Pour ce faire, je l’ai fait dans le service CounterResource en définissant un contrat d’opération distinct pour chaque version ( un pour la version XML et un autre pour la version JSON), comme illustré ici :
...
[WebGet(UriTemplate=" »)]
[OperationContract]
public Counter GetItemInXml()
{
return HandleGet();
}
[WebGet(UriTemplate = « ?format=json », ResponseFormat=WebMessageFormat.Json)]
[OperationContract]
public Counter GetItemInJson()
{
return HandleGet();
}
...
Cela signifie malheureusement que pour chaque opération logique, vous avez besoin de deux contrats d’opération si vous souhaitez prendre en charge les formats XML et JSON. Dans le cas du service CounterResource, j’ai dû disposer de six contrats d’opération pour prendre en charge à la fois XML et JSON pour les opérations GET, POST et PUT.
WCF 4 rend ce scénario beaucoup plus facile à gérer en fournissant la prise en charge de la sélection automatique du format basée sur les en-têtes HTTP « Accepter », ce qui est une meilleure façon de procéder. Permettez-moi de vous expliquer comment cela fonctionne.
Tout d’abord, nous n’avons besoin que d’un seul contrat d’opération pour chaque opération logique :
[WebGet(UriTemplate=" »)]
[OperationContract]
public Counter GetItem()
{
return HandleGet();
}
Ensuite, nous allons sélectionner automatiquement le format sur le webHttpEndpoint standard, comme illustré ici :
<configuration>
<system.serviceModel>
<standardEndpoints>
<webHttpEndpoint>
<-- le point de terminaison standard « » est utilisé pour créer automatiquement un point de terminaison web. -->
<standardEndpoint name=" » helpEnabled="true »
automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</Configuration>
Avec cela en place, le service est désormais capable de gérer et de retourner des messages XML ou JSON. Il détermine le format à utiliser en examinant d’abord l’en-tête HTTP Accept trouvé dans le message de demande. Si cela ne fonctionne pas, il utilise le même format de message que le message de demande, en supposant qu’il s’agit de XML ou JSON, sinon il utilise le format par défaut pour l’opération spécifique.
Il appartient maintenant au client de déterminer le format à utiliser via les en-têtes Http Content-Type et Accept. L’en-tête Content-Type spécifie le format dans le message de demande, tandis que l’en-tête Accept indique le format que le client « accepte » du service. Par exemple, si le client souhaite recevoir json du service, il doit spécifier l’en-tête HTTP Accept suivant dans la demande :
Accept: application/json
Cela est facile à réaliser dans n’importe quel modèle de programmation client HTTP et il est également facile de le faire à l’aide d’outils tels que Fiddler. La figure 21 montre comment utiliser Fiddler pour récupérer JSON à partir de notre nouveau service et améliorer.
Figure 21 : Utilisation de l’en-tête Accept HTTP pour récupérer JSON
WCF 4 fournit également de nouvelles API qui facilitent la spécification explicite du format de message au moment de l’exécution. Le code suivant montre comment étendre l’opération GetItem en écrivant du code qui recherche d’abord un paramètre de chaîne de requête « format » et définit explicitement le format de réponse en conséquence. S’il ne trouve pas de paramètre « format », il s’appuie simplement sur l’en-tête Accepter comme auparavant.
[WebGet(UriTemplate=" »)].
[OperationContract]
public Counter GetItem()
{
si un paramètre de chaîne de requête de format a été spécifié,
définissez le format de réponse sur celui-ci. Si tel n’est pas le cas
paramètre de chaîne de requête existe, l’en-tête Accept sera utilisé
string formatQueryStringValue =
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[
« format"];
si (!string. IsNullOrEmpty(formatQueryStringValue))
{
if (formatQueryStringValue.Equals(« xml »,
System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml ;
}
else if (formatQueryStringValue.Equals(« json »,
System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
else
{
lève la nouvelle chaîne> WebFaultException<(string). Format(« Format non pris en charge '{0}' »,
formatQueryStringValue), HttpStatusCode.BadRequest);
}
}
retour HandleGet();
}
En fin de compte, ces nouvelles fonctionnalités vous permettent de ne plus avoir à coder en dur un type de format de message dans vos contrats d’opération [WebGet] et [WebInvoke] comme vous l’avez fait dans WCF 3.5.
Gestion des erreurs RESTful
Étant donné que les services RESTful n’utilisent pas SOAP, vous ne disposez plus du mécanisme d’erreur SOAP standard, ce qui permet de mapper automatiquement les exceptions .NET et les messages d’erreur SOAP. Lors de la création de services REST dans WCF 3.5, vous deviez construire manuellement le message de réponse HTTP chaque fois que vous vouliez personnaliser le message d’erreur HTTP renvoyé au client.
Dans WCF 4, vous trouverez une nouvelle classe appelée WebFaultException<T> qui facilite considérablement la transmission des « erreurs web » (par exemple, les erreurs HTTP) à vos consommateurs. Il fonctionne très bien comme FaultException<T> dans WCF, mais vous pouvez spécifier un code HTTP status et un type de détail pour fournir plus de détails.
L’exemple suivant montre comment retourner une erreur HTTP 400 (requête incorrecte) lorsque l’utilisateur spécifie un type de format non pris en charge . J’utilise simplement la chaîne pour le type de détail :
si (!string. IsNullOrEmpty(format))
{
si (format. Equals(« xml », System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml ;
}
else if (format. Equals(« json », System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
else
{
lève la nouvelle chaîne> WebFaultException<(string). Format(« Format non pris en charge '{0}' »,
format), HttpStatusCode.BadRequest) ;
}
}
Cette nouvelle fonctionnalité rend beaucoup plus naturel le retour des messages d’erreur HTTP standard en lisant simplement des exceptions comme vous le faites normalement dans vos services SOAP.
Intégration de WCF à ASP.NET Routes
Il existe de nombreuses similitudes entre les fonctionnalités de routage basées sur une URL trouvées dans WCF 4 et ASP.NET 4. Ils vous permettent tous deux de faire la même chose: essentiellement mapper des modèles d’URL à des méthodes sur des classes. Il existe cependant une différence significative entre les deux modèles.
L’approche WCF 4 lie la conception du modèle d’URL à une classe unique via les attributs [WebGet] et [WebInvoke] appliqués à la définition de classe pendant le développement. À mesure que le service croît et évolue au fil du temps, cela peut entraîner une conception monolithique de grande taille qui ne peut pas être prise en compte en segments plus petits. L’approche ASP.NET 4, d’autre part, dissocie la logique de routage de la définition de classe cible, ce qui vous permet de mapper vos itinéraires de service entre de nombreuses définitions de classe lorsque vous le souhaitez.
WCF 4 offre désormais la possibilité d’intégrer le moteur de routage ASP.NET à vos services WCF 4, ce qui permet de tirer parti du modèle de routage ASP.NET par-dessus vos services WCF.
Pour ce faire, utilisez votre méthode Global.asax RegisterRoutes en tirant parti de la nouvelle classe ServiceRoute qui vous permet de mapper un itinéraire ASP.NET à une classe de service WCF :
private void RegisterRoutes()
{
Fabrique WebServiceHostFactory = new WebServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute(« Bookmarks », factory,
typeof(BookmarkService)));
RouteTable.Routes.Add(new ServiceRoute(« Users », factory,
typeof(UserService)));
}
J’appelle RouteTable.Routes.Add deux fois pour ajouter de nouveaux itinéraires pour deux classes de service WCF différentes. Le premier itinéraire mappe « /Bookmarks » à la classe BookmarkService tandis que le second itinéraire mappe « /Users » à la classe UserService. Notez comment les deux itinéraires sont configurés pour utiliser WebServiceHostFactory.
À présent, lorsque nous utilisons les attributs [WebGet] et [WebInvoke] sur les définitions de classe de service WCF, nous utilisons des chemins d’accès relatifs, qui seront relatifs à l’itinéraire ASP.NET spécifié ici.
Modèles de projet REST
L’une des fonctionnalités soignées de Visual Studio 2010 est le Gestionnaire d’extensions, auquel vous pouvez accéder à partir d’Outils | Gestionnaire d’extensions. Le Gestionnaire d’extensions permet à Microsoft et à d’autres tiers d’intégrer de nouveaux modèles de projet, contrôles et outils à votre expérience Visual Studio 2010 en un simple clic. Pour les équipes de produits Microsoft, c’est génial, car cela permet d’expédier des objets après RTM et d’en faire des extensions officielles fournies par Microsoft.
Si vous affichez le Gestionnaire d’extensions et développez le nœud « Modèles », vous trouverez un nœud enfant nommé « WCF ». Cliquez sur « WCF » et vous devriez voir quatre nouveaux modèles de service REST WCF : un pour chaque version de .NET (3.5 vs 4.0) et un par langue (C# vs. VB.NET) comme illustré dans la figure 22.
Figure 22 : Nouveaux modèles de projet REST WCF dans le gestionnaire d’extensions
Vous pouvez sélectionner ces nouveaux modèles de projet et les télécharger dans votre installation locale de Visual Studio 2010 et les utiliser comme n’importe quel autre type de projet. Vous trouverez le nouveau type de projet REST sous Visual C# | Web | Application de service REST WCF (en supposant que vous avez installé la version C#).
Lorsque vous l’utilisez pour créer un projet, vous remarquerez que l’application de service REST WCF qu’elle génère utilise la plupart des nouvelles fonctionnalités WCF 4 que j’ai mises en évidence tout au long de cette section.
Informations complémentaires
En plus de ce que nous avons abordé ici, WCF 4 est fourni avec plusieurs fonctionnalités REST plus avancées et de nouvelles extensions d’API WCF qui simplifient des choses telles que la gestion des formats de messages personnalisés (et non XML ou JSON), le retour de « vues » basées sur des modèles à l’aide de la nouvelle fonctionnalité T4, l’implémentation de la prise en charge conditionnelle GET et ETag, l’implémentation de la concurrence optimiste et la génération de liens sortants.
Pour plus d’informations sur toutes ces nouvelles fonctionnalités WCF 4, y compris celles que nous n’avions pas d’espace à couvrir ici, accédez au blog du point de terminaison .NET et recherchez l’entrée sur Présentation des services web WCF dans .NET 4. L’équipe a fourni une série de blogs complète en 12 parties qui décrit chaque nouvelle fonctionnalité une entrée de blog à la fois, ainsi que de nombreux exemples de code et instructions pas à pas.
Services de workflow
L’un des domaines de fonctionnalités qui ont reçu le plus d’attention dans .NET 4 était celui des « services de flux de travail ». Microsoft a fortement investi dans l’amélioration de l’intégration entre WCF et WF afin de fournir un modèle de programmation riche et déclaratif pour la création d’une grande variété d’applications.
Présentation des services de flux de travail
WF vous donne un modèle de programmation déclaratif qui augmente le niveau d’abstraction pour ceux qui écrivent la logique. Ce que WF vous offre en fin de compte est une infrastructure pour écrire vos propres langages en définissant votre propre bibliothèque d’activités de domaine d’entreprise. Ensuite, vous disposez d’un concepteur visuel pour la composition de ces activités en programmes et d’un runtime qui sait gérer l’exécution de ces programmes.
WF fournit un modèle particulièrement approprié pour l’implémentation d’applications de longue durée qui doivent attendre que différents types d’événements externes se produisent, comme la réception d’un message d’un autre système. Son modèle de persistance permet d’utiliser efficacement les ressources système en conservant uniquement le programme en mémoire lorsqu’il est en cours d’exécution. Lorsque le programme attend qu’un événement externe se produise, il peut être conservé dans la base de données, ce qui libère les ressources système qu’il utilise.
Figure 23 : Workflow Services
Ce qui différencie WF des autres infrastructures de développement, c’est qu’il vous offre ces fonctionnalités tout en vous permettant de programmer à l’aide d’une logique de programmation de contrôle de flux séquentielle, qui est généralement beaucoup plus facile à comprendre pour les développeurs qui lisent et écrivent le code.
Si vous créez des services WCF qui sont de longue durée dans leur nature ou qui pourraient tirer parti de certains des autres avantages que je viens de décrire, vous pouvez implémenter vos services WCF à l’aide d’un flux de travail WF. Vous pouvez également utiliser WF pour coordonner la logique de consommation d’autres services externes.
La figure 23 illustre ces concepts.
Bien que cela soit possible depuis .NET 3.5, .NET 4 a fait des progrès significatifs en améliorant l’expérience des développeurs et en fournissant certaines des fonctionnalités nécessaires manquantes dans .NET 3.5.
En combinant les mondes wcf et WF, vous obtenez le meilleur des deux mondes. Vous bénéficiez d’un modèle de programmation déclaratif, d’une expérience conviviale pour les développeurs grâce aux concepteurs WF, d’un runtime puissant pour la gestion des services de longue durée et de la grande flexibilité de communication offerte par WCF.
Création de votre premier service de flux de travail
Pour vous donner une idée de la nouvelle expérience des développeurs de services de flux de travail, je vais vous guider à travers un exemple complet de création d’un à l’aide de .NET 4 et du nouveau concepteur Visual Studio 2010.
Si vous ouvrez Visual Studio 2010 et sélectionnez Fichier | Nouveau projet, vous trouverez un nouveau projet de flux de travail sous les modèles WCF, appelé « Application de service de flux de travail WCF ». Je vais sélectionner ce modèle de projet et lui donner un nom de « HelloWorldWorkflowService », puis créer le nouveau projet.
Figure 24 : Modèles de projet Des services de flux de travail Visual Studio 2010
Une fois créé, le nouveau projet contient simplement deux fichiers : un fichier Service1.xamlx contenant la définition de service de workflow déclaratif et un fichier Web.config contenant la configuration du service.
L’une des choses les plus convaincantes concernant le nouveau modèle de services de flux de travail dans .NET 4 est que la définition de service entière peut être définie en XAML. Dans notre nouveau projet, le fichier Service1.xamlx contient la définition de service et l’implémentation est simplement un workflow XAML. Le fichier Web.config contient la configuration du service WCF: c’est là que vous allez définir les points de terminaison et les comportements du service de flux de travail.
La figure 25 montre à quoi ressemble le concepteur Visual Studio 2010 pour le fichier Service1.xamlx. Notez qu’il s’agit uniquement du concepteur de flux de travail standard. Dans ce cas précis, le flux de travail que nous concevons servira d’implémentation de service WCF. La clé de l’intégration de cette définition de flux de travail à WCF est le nouveau WorkflowServiceHost et l’ensemble des activités « Messagerie » WCF (voir la boîte à outils de la figure 25).
Figure 25 : Conception d’un service de flux de travail dans Visual Studio 2010
Le service de flux de travail squelette fourni avec ce modèle de projet contient une activité De réception suivie d’une activité d’envoi. Notez que l’activité Receive contient une propriété OperationName et qu’elle est actuellement définie sur « GetData ». Il a également une propriété Content pour lier le message entrant à une variable locale définie dans l’activité Sequence ( dans ce cas, la variable est appelée « data » et est de type Int32 (voir la fenêtre Variables sous le service de conception de flux de travail dans la Figure 25).
L’activité De réception est également configurée pour activer le service, ce qui signifie que dans le message entrant peut entraîner la création d’une nouvelle instance du flux de travail. Notez que la propriété CanCreateInstance est cochée dans le Fenêtre Propriétés à droite de la figure 25).
Personnalisons un peu ce service de workflow. Tout d’abord, je change le nom de fichier du service de Service1.xamlx en HelloWorld.xamlx. Ensuite, je vais remplacer le nom du service par « HelloWorldService » dans la fenêtre appropriée. Ensuite, je vais remplacer la propriété OperationName de l’activité Receive par « SayHello ». Enfin, je vais définir une nouvelle variable appelée « personName » de type String dans la séquence.
Ensuite, je lierai la propriété Contenu de l’activité Receive à la variable « personName », comme illustré dans la figure 26. Ensuite, je vais lier la propriété Contenu de l’activité Envoyer à une chaîne au format « hello {0}! » insertion de la variable personName dans l’espace réservé. Ensuite, j’enregistrerai le fichier Service1.xamlx résultant.
Figure 26 : Définition de la propriété Content pour l’activité Receive
À ce stade, ouvrez le fichier HelloWorld.xamlx et inspectez son contenu. Pour ce faire, cliquez avec le bouton droit sur le fichier dans Explorateur de solutions et sélectionnez « Ouvrir avec... » suivi de « Éditeur XML ». Cela vous permet d’examiner la définition XAML brute du service. Il est important de noter que la définition XAML représente l’implémentation complète du service. Il n’y a pas de code C# du tout.
Pour cet exemple, nous allons nous appuyer sur le point de terminaison HTTP par défaut et supposer que nous avons un comportement de service standard en place qui active automatiquement les métadonnées de service afin que nous n’ayons besoin d’aucune configuration WCF.
Nous sommes maintenant prêts à tester notre service de workflow XAML. C’est aussi simple que d’appuyer sur F5 dans Visual Studio 2010. Appuyez sur F5 pour charger le service de workflow dans le serveur de développement ASP.NET et provoquer l’affichage du client de test WCF. Le client de test WCF se connecte automatiquement au service de flux de travail, télécharge les métadonnées WSDL et permet de tester la logique du service de flux de travail (voir la figure 27).
Figure 27 : Test du service de flux de travail
Cela montre que l’infrastructure des services de flux de travail est en mesure de produire dynamiquement une définition WSDL pour le service en examinant les activités d’envoi et de réception utilisées dans la définition de flux de travail et les types de messages qu’elles envoient et reçoivent.
Cela complète la procédure pas à pas simple de la création de votre premier service de flux de travail déclaratif dans .NET 4. Nous avons vu que l’implémentation du service est entièrement définie en XAML et que vous utilisez les activités d’envoi/réception pour modéliser les interactions de messagerie dans le flux de travail. Nous avons également vu que .NET 4 est fourni avec la prise en charge de l’hébergement pour les fichiers .xamlx, supprimant ainsi la nécessité de fichiers .svc supplémentaires. Nous allons continuer à explorer chacune de ces zones plus en détail tout au long des sections suivantes.
Hébergement de services de workflow
.NET 4 est fourni avec une infrastructure d’hébergement nouvelle et améliorée pour les services de flux de travail. Vous pouvez héberger des services de flux de travail dans IIS/WAS ou dans vos propres applications à l’aide de WorkflowServiceHost. L’infrastructure d’hébergement .NET 4 fournit les éléments nécessaires à la gestion des instances de workflow de longue durée et à la corrélation des messages lorsque plusieurs instances de service s’exécutent simultanément. .NET 4 fournit également un point de terminaison de contrôle de flux de travail standard pour la gestion à distance des instances de workflow.
Hébergement dans IIS/ASP.NET
L’exemple HelloWorldWorkflowService simple que nous avons parcouru dans la section précédente illustre comment héberger des services de flux de travail dans IIS/ASP.NET. Bien que nous ayons testé le service à l’aide du serveur de développement ASP.NET, il aurait fonctionné de la même façon dans IIS à l’aide d’un pool d’applications ASP.NET 4. .NET 4 installe le gestionnaire nécessaire pour les requêtes .xamlx, qui gère la création de WorkflowServiceHost en arrière-plan et l’activation des instances de workflow individuelles.
Bien que nous utilisions l’approche « zéro configuration » dans notre premier exemple, vous pouvez configurer des services de flux de travail dans Web.config comme n’importe quel autre service WCF. C’est là que vous configurez tous les points de terminaison WCF et les comportements que vous souhaitez utiliser. Vous pouvez exposer autant de points de terminaison que vous le souhaitez sur vos services de workflow, mais vous devez envisager d’utiliser l’une des nouvelles liaisons de « contexte » (par exemple, BasicHttpContextBinding, WSHttpContextBinding ou NetTcpContextBinding) qui gèrent instance communication spécifique.
L’exemple suivant montre comment configurer un service de workflow avec deux points de terminaison HTTP :
<configuration>
<system.serviceModel>
<services>
<service name="HelloWorldWorkflowService »>
<endpoint binding="basicHttpContextBinding » contract="IHelloWorld"/>
<endpoint address="ws » binding="wsHttpContextBinding »
contract="IHelloWorld"/>
</service>
...
Vous pouvez également configurer le service de workflow avec des comportements WCF. L’exemple suivant montre comment configurer ce service de workflow avec quelques-uns des comportements WCF standard :
<configuration>
<system.serviceModel>
<services>
<service name="HelloWorldWorkflowService »
behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior » >
<endpoint binding="basicHttpContextBinding » contract="IHelloWorld"/>
<endpoint address="ws » binding="wsHttpContextBinding »
contract="IHelloWorld"/>
</service>
</Services>
<behaviors>
<serviceBehaviors>
<behavior name="HelloWorldWorkflowService.Service1Behavior »>
<serviceDebug includeExceptionDetailInFaults="False » />
<serviceMetadata httpGetEnabled="True"/>
</Comportement>
</serviceBehaviors>
...
En plus de ces comportements WCF standard, .NET 4 est fourni avec de nouveaux comportements spécifiques à WF pour contrôler la persistance des flux de travail, le suivi des flux de travail et d’autres comportements d’exécution de workflow conjointement avec vos services de flux de travail. Pour plus d’informations, consultez les exemples du Kit de développement logiciel (SDK) .NET 4.
Auto-hébergement avec WorkflowServiceHost
Bien que .NET 3.5 soit fourni avec une classe WorkflowServiceHost pour l’hébergement des services de flux de travail, il a dû être repensé en fonction de toutes les modifications apportées au runtime et au modèle de programmation WF dans .NET 4. Par conséquent, .NET 4 est fourni avec une nouvelle classe WorkflowServiceHost trouvée dans l’assembly System.ServiceModel.Activities.
La classe WorkflowServiceHost facilite l’hébergement des services de flux de travail dans votre propre application, qu’il s’agisse d’une application console, d’une application WPF ou d’un service Windows. Le fragment de code suivant illustre comment utiliser WorkflowServiceHost dans votre propre code d’application :
...
Hôte WorkflowServiceHost = new WorkflowServiceHost(« HelloWorld.xamlx »,
new URI(« https://localhost:8080/helloworld"));
Hôte. AddDefaultEndpoints();
Hôte. Description.Behaviors.Add(
new ServiceMetadataBehavior { HttpGetEnabled = true });
Hôte. Open();
Console.WriteLine(« Host is open »);
Console.ReadLine();
...
Comme vous pouvez le voir, que vous choisissiez d’héberger vos services de workflow dans IIS/ASP.NET ou dans vos propres applications, ils sont tout aussi faciles à héberger que n’importe quel service WCF.
WorkflowControlEndpoint
L’hébergement d’un service de flux de travail s’occupe de l’initialisation du runtime WF et de l’activation de nouvelles instances de workflow lors de l’activation des messages arrivant via l’un des points de terminaison exposés. En plus de cette fonctionnalité d’hébergement de base, il est également important de pouvoir gérer à distance la configuration et l’exécution de ces instances de workflow en cours d’exécution. .NET 4 facilite cette tâche en fournissant un WorkflowControlEndpoint standard que vous pouvez exposer sur vos services de flux de travail.
L’exemple suivant montre comment ajouter le point de terminaison de contrôle de workflow standard à votre service dans un scénario d’hébergement personnalisé :
...
Hôte WorkflowServiceHost = new WorkflowServiceHost(« HelloWorld.xamlx »,
new URI(« https://localhost:8080/helloworld"));
Hôte. AddDefaultEndpoints();
WorkflowControlEndpoint wce = new WorkflowControlEndpoint(
new NetNamedPipeBinding(),
new EndpointAddress(« net.pipe://localhost/helloworld/WCE »));
Hôte. AddServiceEndpoint(wce);
Hôte. Open();
...
Si vous hébergez dans IIS/ASP.NET, vous pouvez ajouter le workflowControlEndpoint standard comme suit :
<configuration>
<system.serviceModel>
<services>
<service name="HelloWorldWorkflowService »
behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior » >
<endpoint address=" » binding="basicHttpContextBinding » contract="IHelloWorld"/>
<endpoint address="ws » binding="wsHttpContextBinding » contract="IHelloWorld"/>
<endpoint address="wce » binding="wsHttpBinding » kind="workflowControlEndpoint"/>
</service>
...
WorkflowControlEndpoint permet à votre service de workflow d’être géré dans différents environnements d’hébergement. L’un de ces environnements est rendu possible par Windows Server AppFabric, qui sera disponible dans une version ultérieure de Windows Server.
Activités d’envoi/réception
.NET 3.5 était fourni avec deux activités de messagerie ( Envoyer et Recevoir ) pour envoyer et recevoir des messages à l’aide de WCF, mais elles étaient très limitées en termes de fonctionnalités. .NET 4 améliore les activités d’envoi et de réception avec des fonctionnalités supplémentaires (par exemple, la corrélation) et ajoute quelques activités de messagerie supplémentaires ( SendReply et ReceiveReply ) qui simplifient la modélisation des opérations de requête-réponse.
Lorsque vous utilisez les activités d’envoi/réception au sein d’un service de workflow, vous les utilisez essentiellement pour modéliser le contrat de service qui sera exposé aux clients via la définition WSDL. Lorsque vous utilisez l’activité Receive, vous lui attribuez un nom d’opération et vous mappez le message entrant à un type .NET. Bien que nous utilisions jusqu’à présent des chaînes simples, vous pouvez également utiliser des contrats de données complexes définis par l’utilisateur. Nous allons mettre à jour HelloWorldWorkflowService pour utiliser le type de contrat de données suivant :
[DataContract(Namespace=" »)]
public class Person
{
[DataMember]
id de chaîne publique ;
[DataMember]
chaîne publique FirstName ;
[DataMember]
chaîne publique LastName ;
}
Si nous ajoutons cette définition de classe au projet Web et revenons à HelloWorld.xamlx, nous pouvons ensuite définir une nouvelle variable appelée « personMsg » de type Person (le contrat de données défini ci-dessus). Ensuite, nous pouvons mapper les activités ReceiveRequest et SendResponse à la variable personMsg via la propriété « Content ». Pour ce faire, sélectionnez simplement chaque activité et appuyez sur le bouton « Contenu » pour afficher la fenêtre « Définition de contenu ». Ensuite, entrez « personMsg » dans la zone de texte « Données du message » et « Person » dans la zone de texte « Type de message ». Vous devez le faire pour les deux activités.
Les activités d’envoi/réception permettent également de configurer d’autres aspects du service WCF, tels que le nom de l’opération, le nom du contrat de service, l’action, la collection de types connus, le niveau de protection et le sérialiseur à utiliser au moment de l’exécution (par exemple, DataContractSerializer et XmlSerializer). Dans l’activité Envoyer, vous spécifiez également les détails du point de terminaison cible. Tous ces paramètres sont configurables via le Fenêtre Propriétés lorsque l’activité Envoyer/Recevoir est sélectionnée.
Une fois ces modifications en place, vous pouvez tester à nouveau HelloWorld.xamlx pour voir que l’opération SayHello est désormais définie pour recevoir et retourner des messages de type Person.
Définition des opérations Request-Reply
Afin de faciliter la modélisation des opérations demande-réponse, .NET 4 introduit quelques nouvelles activités pour la modélisation de ces types d’interactions. L’une d’elles est l’activité SendReply que vous pouvez combiner avec une activité Receive pour implémenter une opération de demande-réponse au sein du service. Il existe également une activité ReceiveReply que vous pouvez combiner avec une activité d’envoi lors de l’appel d’un service externe dans un workflow.
.NET 4 est également fourni avec quelques activités de niveau supérieur appelées ReceiveAndSendReply et SendAndReceiveReply, que vous verrez dans la boîte à outils Visual Studio 2010. Ces activités composent simplement Réception/Envoi avec SendReply/ReceiveReply respectivement et les rendent faciles à utiliser. Lorsque vous les faites glisser sur l’aire de conception du workflow, vous voyez qu’elles se développent vers une séquence qui contient une activité d’envoi ou de réception suivie de l’activité « réponse » appropriée.
Ajouter une référence de service
Pour faciliter encore plus la consommation de services externes, Visual Studio 2010 fournit également une fonctionnalité « Ajouter une référence de service » qui fonctionne comme prévu, sauf qu’au lieu de générer une définition de classe de proxy client, il génère un ensemble d’activités côté client. Après avoir sélectionné « Ajouter une référence de service » et spécifié l’adresse de la définition WSDL, Visual Studio télécharge le WSDL et génère une activité personnalisée pour chaque opération trouvée dans la définition WSDL.
Examinons un exemple simple. Supposons qu’il existe un CalculatorService avec les opérations Ajouter, Soustraire, Multiplier et Diviser que nous voulons utiliser à partir de notre service de flux de travail. Nous pouvons simplement sélectionner « Ajouter une référence de service » et spécifier l’emplacement de la définition WSDL CalculatorService, comme illustré dans la figure 28.
Figure 28 : Ajouter une référence de service
Une fois que nous appuyons sur OK pour ajouter la référence de service, Visual Studio télécharge la définition WSDL et génère quatre activités personnalisées qui apparaîtront dans la boîte à outils. Vous disposez maintenant d’activités Ajouter, Soustraire, Multiplier et Diviser qui sont faciles à utiliser dans les flux de travail pour appeler le service externe.
Corrélation
Lors de la création de systèmes constitués de services de flux de travail de longue durée, il est courant que de nombreuses instances d’un service de workflow unique s’exécutent simultanément en attendant que le même événement se produise (par exemple, en attendant qu’un message spécifique arrive avant de continuer). Le défi de ce scénario est que vous avez besoin d’un moyen de mettre en corrélation le message entrant avec le bon instance de flux de travail. En règle générale, la façon dont vous déterminez le bon instance de flux de travail consiste à inspecter le contenu du message entrant.
.NET 4 est fourni avec une fonctionnalité sophistiquée de corrélation de messages basée sur le contenu que vous pouvez utiliser conjointement avec les activités d’envoi et de réception que nous venons d’aborder. L’implémentation de cette fonctionnalité tourne autour de ce que l’on appelle les « handles de corrélation », un autre nouveau concept dans .NET 4.
Pour commencer à utiliser la corrélation, vous devez d’abord définir une variable de workflow de type CorrelationHandle. Vous pouvez considérer cette variable comme le point de rendez-vous pour connecter un élément de données à partir (potentiellement) de deux messages différents (en cours de traitement par deux activités de messagerie différentes). Les activités d’envoi et de réception fournissent une propriété CorrelationsWith permettant de spécifier une variable CorrelationHandle.
Pour mettre en corrélation les messages envoyés par plusieurs activités d’envoi/réception, vous devez spécifier le même handle de corrélation pour toutes les activités qui souhaitent participer à la corrélation. Ensuite, sur chaque activité, vous configurez la clé de corrélation qui sera mappée à sur le message en cours de traitement par cette activité particulière (par exemple, l’élément SSN d’un message peut être corrélé avec l’élément CustomerId dans un autre message). Vous définissez des clés de corrélation à l’aide d’expressions XPath qui sont évaluées par rapport aux messages.
Étendons notre HelloWorldWorkflowService pour voir un exemple de fonctionnement. Actuellement, l’exemple avec lequel nous travaillons reçoit un message Person et le retourne au client. Nous allons ajouter une autre activité ReceiveAndSendReply juste en dessous de l’activité SendResponse en bas du flux de travail. Cela permet d’ajouter sequence qui contient une autre réception suivie d’un autre SendReply. Je vais donner à l’activité De réception un nom d’opération « Terminer », et nous allons demander au client de fournir une salutation dans le message Terminer que nous utiliserons pour construire la réponse de salutation finale.
En d’autres termes, les clients appellent d’abord SayHello en fournissant leur nom complet pour lancer un nouveau service de workflow instance. Ensuite, le instance de flux de travail attendra que le client appelle Terminer en fournissant une salutation (ce qui peut prendre des minutes, des heures ou des jours, pendant lesquels le flux de travail peut persister). Une fois que le client a appelé Terminer pour fournir la salutation, le workflow génère un message d’accueil à l’aide de la salutation et du nom d’origine et le retourne. Dans ce scénario, nous pouvons facilement avoir plusieurs instances de workflow en cours d’exécution en attente de l’appel de l’opération Finish. Nous devrons donc certainement mettre en corrélation le message Finish avec le message SayHello précédent. Étant donné que c’est le cas, je dois associer l’activité Receive pour « Finish » au même handle de corrélation que celui que nous avons spécifié sur l’activité « SayHello » (nameHandle).
Examinons maintenant le contenu du message que nous allons mettre en corrélation entre ces deux activités. Pour cet exemple, je vais utiliser les deux types de contrats de données suivants pour modéliser les messages :
[DataContract(Namespace=" »)]
public class Person
{
[DataMember]
chaîne publique FirstName { get; set; }
[DataMember]
chaîne publique LastName { get; set; }
}
[DataContract(Namespace = « »)]
salutation de la classe publique
{
[DataMember]
public string Salutation { get; set; }
[DataMember]
public string NameKey { get; set; }
}
L’activité Receive pour « SayHello » est configurée pour utiliser le message Person et l’activité Receive pour « Finish » est configurée pour utiliser le message d’accueil. Nous allons supposer que la valeur LastName est toujours unique afin de pouvoir l’utiliser comme valeur unique pour la corrélation. Par conséquent, lorsque le client envoie le message « Finish », il doit fournir la même valeur LastName que celle utilisée dans le message « SayHello » précédent.
Voyons maintenant comment configurer le handle de corrélation pour le configurer. J’ai déjà défini une variable CorrelationHandle dans la séquence nommée « handle ». La première chose que nous devons faire est d'« initialiser » le handle de corrélation au sein de l’activité réception « SayHello ». Sélectionnez donc l’activité De réception « SayHello » et appuyez sur le bouton en regard de la zone de texte « CorrelationInitializers ».
Ici, vous devez sélectionner « Initialiseur de corrélation de requête » dans la zone de liste déroulante, puis vous devez être en mesure de choisir la propriété « LastName » pour le champ de requête (cela doit produire une expression XPath de « sm:body()/xg0:Person/xg0:LastName », comme illustré dans la figure 29).
Ensuite, nous devons spécifier que l’activité de réception « Terminer » avec est corrélée sur le même handle. Sélectionnez l’activité de réception « Terminer » et appuyez sur le bouton « CorrélerOn ». Spécifiez ensuite « handle » pour le handle « CorrélatsWith », puis sélectionnez « NameKey » pour le champ de requête (voir figure 30).
Figure 29 : Définition d’une clé de corrélation pour « SayHello »
Figure 30 : Définition d’une clé de corrélation pour « Terminer »
Cela signifie finalement que l’élément LastName dans le message Person doit correspondre à l’élément NameKey dans le message Greeting pour les deux demandes distinctes. Une fois cela en place, l’infrastructure de flux de travail sera en mesure de mettre en corrélation automatiquement les messages pour nous et d’acheminer les messages « Finish » entrants vers le instance de flux de travail correct (en fonction de « LastName/NameKey »).
L’activité SendReply finale retourne la chaîne mise en forme suivante à l’appelant, y compris les informations du « personMsg » d’origine et du nouveau « greetingMsg » :
String.Format(« {0}{1}{2}! »,
greetingMsg.Salutation, personMsg.FirstName, personMsg.LastName)
Je vais tester la fonctionnalité de corrélation à l’aide du client de test WCF pour démarrer plusieurs instances de flux de travail en appelant SayHello plusieurs fois avec différentes valeurs FirstName et LastName. Après cela, nous devons disposer de plusieurs instances en cours d’exécution du service de flux de travail. À présent, nous pouvons appeler l’opération Finish en spécifiant l’une des mêmes valeurs LastName utilisées dans l’un des messages SayHello et nous devons voir la valeur FirstName correspondante utilisée dans le message d’accueil final retourné au client (voir figure 31).
Cela illustre la corrélation basée sur le contenu en action, une fonctionnalité attrayante des services de flux de travail fournie avec .NET 4. Il est important de noter que vous pouvez également effectuer une corrélation basée sur des données au niveau du canal et du protocole, comme vous l’avez peut-être fait dans .NET 3.5 (par exemple, à l’aide du canal ou du workflow instance ID). La corrélation basée sur le contenu est une approche plus flexible et sophistiquée pour faire la même chose.
Figure 31 : Test de l’exemple de corrélation basée sur le contenu
Cela nous amène à la fin de la couverture de nos services de flux de travail. Nous n’avons pu que gratter la surface des services de flux de travail dans ce document, mais vous trouverez des informations supplémentaires dans les exemples du KIT de développement logiciel (SDK) .NET 4 et dans la documentation MSDN croissante disponible en ligne. Comme vous pouvez le voir, la combinaison de WCF et de WF 4 ouvre les portes à un tout nouveau modèle de développement pour la création de services déclaratifs, de longue durée et asynchrones qui peuvent bénéficier des meilleures fonctionnalités que les deux frameworks ont à offrir.
Fonctionnalités avancées diverses
En plus de tout ce dont nous avons parlé dans ce document, WCF 4 est fourni avec quelques fonctionnalités plus avancées que vous pouvez trouver utiles. Ces fonctionnalités avancées incluent une prise en charge améliorée de la résolution de type via DataContractResolver, la possibilité de gérer des consommateurs de file d’attente concurrents à l’aide de ReceiveContext, d’un nouvel encodeur de flux d’octets et d’un suivi haute performance basé sur ETW.
Résolution de type avec DataContractResolver
Dans WCF 3.x, il existe une fonctionnalité de résolution de type appelée « types connus ». Lors de la désérialisation, lorsque le sérialiseur rencontre un instance qui n’est pas du même type que le type déclaré, il inspecte la liste des « types connus » déclarés pour déterminer le type à utiliser. En tant qu’auteur du service, vous pouvez annoter vos types/méthodes avec les attributs [KnownType] ou [ServiceKnownType] pour définir la liste des substitutions possibles. Cette fonctionnalité est couramment utilisée pour prendre en charge l’héritage et le polymorphisme.
Malheureusement, WCF 3.x ne fournit pas un moyen simple de remplacer l’algorithme de mappage de type utilisé par DataContractSerializer lors de l’exécution de ce type de résolution de type de type dynamique au moment de l’exécution. Pour résoudre ce problème, WCF 4 fournit la classe DataContractResolver abstraite à partir de laquelle vous pouvez dériver pour implémenter votre propre algorithme de résolution de type personnalisé. Voici comment commencer :
classe MyDataContractResolver : DataContractResolver
{
Assembly ;
public MyDataContractResolver(Assembly)
{
this.assembly = assembly ;
}
Utilisé lors de la désérialisation
Permet aux utilisateurs de mapper le nom xsi:type à n’importe quel type
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver)
{
... // implémentez votre mappage
}
Utilisé lors de la sérialisation
Mappe n’importe quel type à une nouvelle représentation xsi:type
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver, out XmlDictionaryString typeName,
out XmlDictionaryString typeNamespace)
{
... // implémentez votre mappage
}
}
Une fois que vous avez implémenté votre DataContractResolver personnalisé, vous pouvez le fournir à DataContractSerializer, comme illustré ici :
DataContractSerializer dcs = new DataContractSerializer(
typeof(Object), null, int. MaxValue, false, true, null,
new MyDataContractResolver(assembly));
À présent, lorsque vous utilisez ce DataContractSerializer instance pour sérialiser/désérialiser des objets, votre DataContractResolver personnalisé est appelé pour effectuer la résolution de type personnalisée.
Pour injecter votre DataContractResolver personnalisé dans le runtime WCF en arrière-plan, vous devez écrire un comportement de contrat WCF qui se connecte au DataContractSerializerOperationBehavior et remplace le programme de résolution par défaut. Le Kit de développement logiciel (SDK) .NET 4 est fourni avec un exemple complet qui illustre comment y parvenir via un comportement de contrat et un attribut personnalisé appelé [KnownAssembly].
La résolution de type personnalisée peut être utile lorsque vous souhaitez remplacer les valeurs par défaut de DataContractSerializer, contrôler exactement quels types sont utilisés pour la sérialisation ou gérer dynamiquement les types connus.
Traitement des messages mis en file d’attente avec ReceiveContext
Avec WCF 3.x, vous pouvez garantir la remise en une seule fois des messages lors de l’utilisation de NetMsmqBinding en utilisant des files d’attente transactionnelles et en inscrivant l’opération de service WCF dans la transaction MSMQ. Lorsque des exceptions sont levées lors du traitement d’un message, WCF s’assure que le message n’est pas perdu en renvoyant le message à une file d’attente (il peut être renvoyé à la file d’attente d’origine, à une file d’attente de messages incohérents ou à une file d’attente de lettres mortes en fonction de la configuration).
Bien que cette fonctionnalité soit importante, il existe quelques problèmes que les gens rencontrent fréquemment. La première est que les transactions sont coûteuses et qu’elles produisent beaucoup de lecture/écriture avec les files d’attente, ce qui introduit plus de surcharge et de complexité. Un autre problème est qu’il n’existe aucun moyen de s’assurer que le même service traite le message la prochaine fois qu’il sera extrait de la file d’attente (par exemple, il n’existe aucun moyen pour un service particulier de « verrouiller » un message), ce qui est une garantie que vous pouvez souhaiter faire dans certains scénarios.
Pour résoudre ces problèmes, WCF 4 introduit une nouvelle API appelée ReceiveContext pour le traitement des messages en file d’attente. Avec ReceiveContext, un service peut « consulter » un message dans la file d’attente pour commencer à le traiter et si un problème se produit et qu’une exception est levée, il reste dans la file d’attente. Les services peuvent également verrouiller des messages afin de réessayer de les traiter ultérieurement. ReceiveContext fournit un mécanisme pour « terminer » le message une fois qu’il a été traité afin qu’il puisse être supprimé de la file d’attente.
Cette approche simplifie les choses sur plusieurs fronts, car les messages ne sont plus lus et réécrits dans les files d’attente sur le réseau, et les messages individuels ne rebondisent pas entre différentes instances de service pendant le traitement. Examinons un exemple simple pour illustrer son fonctionnement.
L’exemple suivant montre comment utiliser ReceiveContext pour traiter les messages arrivant dans une file d’attente :
...
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
[ReceiveContextEnabled(ManualControl = true)]
public void CalculateProduct(int firstNumber, int secondNumber)
{
ReceiveContext receiveContext ;
si (! ReceiveContext.TryGet(OperationContext.Current.IncomingMessageProperties,
out receiveContext))
{
Console.WriteLine(« ReceiveContext not installed/found on this machine. »);
return;
}
if ((firstNumber * secondNumber) % 2 == (receiveCount % 2))
{
receiveContext.Complete(TimeSpan.MaxValue) ;
Console.WriteLine( »{0} x {1} = {2}« , firstNumber, secondNumber,
firstNumber * secondNumber);
}
else
{
receiveContext.Abandon(TimeSpan.MaxValue) ;
Console.WriteLine( »{0}&{1} not processed », firstNumber, secondNumber) ;
}
receiveCount++;
}
...
En supposant une réussite, nous appelons ReceiveContext.Complete pour terminer le traitement du message, ce qui entraîne sa suppression de la file d’attente sous-jacente. Sinon, nous appelons Abandon, ce qui laisse le message sur la file d’attente pour de futures nouvelles tentatives. Le Kit de développement logiciel (SDK) .NET 4 est fourni avec un exemple complet que vous pouvez utiliser pour explorer cette nouvelle fonctionnalité plus en profondeur.
Simplifier l’encodage avec ByteStreamMessageEncodingBindingElement
Dans certains scénarios de messagerie, vous souhaitez simplement transmettre des « objets blob » de données binaires sans aucun encapsulage ou traitement supplémentaire. Afin de simplifier ce scénario, WCF 4 est fourni avec un nouveau ByteStreamMessageEncodingBindingElement qui effectue exactement cette opération. Malheureusement, .NET 4 n’est pas doté d’une nouvelle liaison pour cet encodeur. Vous devrez donc créer une liaison personnalisée pour pouvoir l’utiliser.
Le Kit de développement logiciel (SDK) .NET 4 est fourni avec un exemple complet qui montre comment définir une liaison personnalisée qui utilise byteStreamMessageEncodingBindingElement via une classe de liaison personnalisée. Une fois la nouvelle classe de liaison en place, l’encodeur de flux d’octets devient très facile à utiliser avec vos services.
Suivi haute performance basé sur ETW
WCF 4 apporte également des améliorations sur le suivi et le diagnostics front. WCF 4 utilise désormais ETW pour le suivi, ce qui améliore considérablement les performances de suivi et offre une meilleure intégration avec d’autres technologies connexes telles que Windows Workflow Foundation, Windows Server AppFabric et les différentes technologies de gestion présentes dans Windows Server, offrant ainsi un meilleur modèle pour la plateforme.
Avec ETW, les fournisseurs d’événements écrivent des événements dans des sessions et les sessions peuvent éventuellement conserver ces événements dans un journal. Les consommateurs peuvent écouter les événements de session en temps réel ou lire ces événements à partir d’un journal après coup. Les contrôleurs ETW sont responsables de l’activation/de la désactivation des sessions et de l’association des fournisseurs à des sessions.
Le Kit de développement logiciel (SDK) .NET 4 fournit un exemple simple qui montre comment tirer parti de cette nouvelle architecture de suivi hautes performances conjointement avec vos services WCF.
Conclusion
WCF 4 apporte de nombreuses améliorations et plusieurs fonctionnalités totalement nouvelles qui répondent à certains des scénarios de communication les plus courants d’aujourd’hui. Tout d’abord, WCF 4 devient plus facile à utiliser grâce au modèle de configuration simplifié et à une meilleure prise en charge des valeurs par défaut courantes.
En outre, WCF 4 fournit une prise en charge de première classe pour la découverte et le routage des services, qui sont des exigences courantes dans la plupart des environnements d’entreprise et des grandes initiatives SOA. Ces fonctionnalités seules distinguent WCF de la plupart des infrastructures concurrentes. WCF 4 simplifie également le développement de services BASÉS sur REST et fournit plusieurs autres fonctionnalités WCF plus avancées qui répondent aujourd’hui à certains problèmes spécifiques.
En plus de tout cela, WCF 4 fournit une intégration sophistiquée à WF afin de fournir un nouveau modèle pour le développement de services de flux de travail déclaratifs. Les services de flux de travail permettent de développer des services asynchrones et de longue durée qui tirent parti du modèle de programmation WF et du runtime sous-jacent. Grâce aux nouvelles activités basées sur WCF et à la prise en charge du concepteur dans Visual Studio 2010, ce nouveau modèle de programmation devient une option de première classe pour les services de création.
Dans .NET 4, les mondes de WCF et de WF fusionnent pour offrir un modèle de programmation cohérent qui vous offre le meilleur des deux mondes. Pour plus d’informations sur les nouveautés de WF 4, case activée présentation d’un développeur à Windows Workflow Foundation dans .NET 4.
À propos de l’auteur
Aaron Skonnard est cofondateur de Pluralsight, un fournisseur de formation Microsoft qui offre des cours de développement .NET à la demande et dirigés par un instructeur. Ces jours-ci Aaron passe la majeure partie de son temps à enregistrer Pluralsight On-Demand! cours axés sur le cloud computing, Windows Azure, WCF et REST. Aaron a passé des années à écrire, parler et enseigner aux développeurs professionnels du monde entier. Vous pouvez l’atteindre à http://pluralsight.com/aaron et http://twitter.com/skonnard.