Question

J’écris un petit moteur d’hébergement WCF qui créera de manière dynamique ServiceHosts sur la base du fichier .config. L’idée générale est de nous permettre de supprimer les services existants et d’ajouter de nouveaux services au moment de l’exécution sans avoir à mettre tous nos services hors ligne.

J'ai rencontré un test unitaire qui indique que cela pourrait ne pas être aussi facile qu'il y paraît. Il semble qu'un seul ServiceHost puisse exister pour un point de terminaison donné (même si plusieurs points de terminaison différents pour un service peuvent exister dans un même ServiceHost). Ce n'est normalement pas un problème. Cependant, lorsqu'un service doit être reconfiguré, la suppression de ServiceHost d'origine ne supprime pas l'enregistrement de cette adresse de point de terminaison. Essayer de créer un autre ServiceHost pour le même service (ce qui signifie que les mêmes ordinateurs d'extrémité sont utilisés) échoue avec l'exception suivante:

System.InvalidOperationException: The ChannelDispatcher at 'net.pipe://localhost/' with contract(s) '"ITestService"' is unable to open its IChannelListener. --->
System.InvalidOperationException: A registration already exists for URI 'net.pipe://localhost/'.

Je rencontre effectivement l'erreur lors des tests unitaires. Les tests permettront d’exercer une unité, ce qui fermera complètement les serveurs ServiceHosts et le serveur d’hébergement le plus humainement possible. Il crée ensuite une autre instance du moteur d’hébergement, qui tente de recréer à nouveau les mêmes ServiceHosts pour un test différent. Le deuxième test rencontre l'erreur ci-dessus. J'imagine que, même si ServiceHost.Close () a été appelé, cela ne détruit pas l'hôte du service ... il reste donc en mémoire. Je ne peux pas dire si le GC est en train de nettoyer les anciens hôtes du service ou non ... le problème persiste sans disparaître après son apparition initiale (du mieux que j'ai pu déterminer ... j'ai attendu environ 30 minutes jusqu'à présent. )

Mon fichier de configuration pour system.serviceModel est le suivant:

  <system.serviceModel>
    <services>
      <service name="Campus.Core.ServiceModel.TestServiceStub">
        <endpoint          
          address="net.pipe://localhost"          
          binding="netNamedPipeBinding"           
          contract="Campus.Core.ServiceModel.ITestService"
        />
      </service>
    </services>
  </system.serviceModel>
Était-ce utile?

La solution

Pour répondre à cette question, au cas où quelqu'un d'autre aurait rencontré le problème. Il s’est avéré qu’il existe deux causes à ce problème:

1) Lors des tests unitaires, si une exception était rencontrée, elle s’écartait généralement du code à tester avant que le ServiceHost ne puisse être fermé. Cela a laissé le ServiceHost lié à un noeud final particulier. Cela a provoqué l'échec de TOUS les tests ultérieurs utilisant le même code. Comme je faisais BDD avec SubSpec et xUnit, un seul cas de test (en termes de BDD) exécutait une seule assertion par test, et un seul cas de test pouvait englober une douzaine ou plus d'assertions.

2) Attention au point de terminaison MEX. Le noeud final MEX ne peut exister qu'une fois par service. Au départ, j'avais créé un point de terminaison http et net.tcp mex. Cela posait cependant un problème, car l’instance du noeud final MEX démarrée en deuxième position renvoyait une exception. De manière générale, si vous utilisez le terminal MEX, HTTP est le protocole le plus utile, sauf s’il existe un problème d’infrastructure physique qui vous en empêche.

De manière générale, l’appel de la méthode Close () sur un ServiceHost le dissociera totalement, ce qui permettra de réutiliser les adresses précédemment liées à ses points de terminaison. Parfois, la fermeture peut prendre un certain temps et, dans de rares cas, une exception peut être levée. Si vous utilisez BDD avec SubSpec et que vous suivez la règle d'assertion unique par test, une exception levée dans un test empêchant la fermeture de ServiceHosts entraînera l'échec de tous les tests ultérieurs.

Autres conseils

Une solution consiste à ajouter un GUID à l’URL de l’hôte de service chaque fois que vous en augmentez une, et à utiliser une approche d’usine permettant de remonter les instances ServiceHost et de renvoyer le canal côté client, de sorte que le client sache laquelle. URL à utiliser.

L'exemple InProcFactory d'IDesign utilise cette approche. Vous pourrez donc l'utiliser tel quel:

http://www.idesign.net/idesign/DesktopDefault .aspx? tabindex = 5 & amp; tabid = 11

Notez que vous devrez vous inscrire sur le site d'IDesign pour pouvoir télécharger l'exemple. Ils vous enverront parfois des annonces concernant la formation, etc., mais ce n'est pas trop.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top