MSDN Magazin > Home > Ausgaben > 2008 > April >  Service Station: Erstellen eines WCF-Routers, T...
Service Station
Erstellen eines WCF-Routers, Teil 1
Michele Leroux Bustamante

Das Hosten und Nutzen eines WCF-Diensts (Windows® Communication Foundation) erfordert meist einige grundlegende Schritte: Implementieren des Diensts; Konfigurieren der Endpunkte, an denen der Dienst erreicht werden kann; Hosten des Diensts; Generieren einer WSDL-Datei (Web Services Description Language) oder Ermöglichen eines Metadatenaustauschs, damit Clients einen Proxy generieren können, um den Dienst aufzurufen; Schreiben von Code, um den Proxy mit seiner zugeordneten Konfiguration zu instanziieren und Beginnen mit dem Aufrufen von Dienstvorgängen. Sie müssen selten unter die Haube sehen, doch selbst im einfachsten Fall verlassen sich sowohl die Client- als auch die Dienstkanäle auf kompatible Konfigurationen, von denen die Adressierungssemantik und die Nachrichtenfilterung verarbeitet werden. Damit wird sichergestellt, dass der richtige Vorgang aufgerufen wird.
Manchmal ist es nützlich, einen Mittler- oder Routerdienst zwischen einem Client und einem Zieldienst einzuführen. Damit werden Nachrichten empfangen, die zwischen den beiden hin- und hergehen, sowie zusätzliche Aktivitäten durchgeführt, z. B. Protokollieren, Prioritätsrouting, Online-/Offlinerouting, Lastenausgleich oder das Einführen einer Sicherheitsgrenze. Wenn solch ein Mittlerdienst eingeführt wird, müssen einige Verhaltensweisen bei der Adressierung und der Nachrichtenfilterung angepasst werden.
Sehen wir uns also genauer an, wie Sie mit Mittlerdiensten arbeiten können, die der Einfachheit halber hier kollektiv als Router bezeichnet werden sollen. In diesem Artikel werden die Konzepte der WCF-Adressierung und Nachrichtenfilterung mit besonderem Augenmerk auf dem Routerszenario erklärt. Außerdem werden einige Optionen für die Routingkonfiguration zusammen mit den entsprechenden Einstellungen erläutert. In Teil 2 dieser Reihe wird gezeigt, wie Sie diese Grundlage für fortgeschrittenere, praktische Routingimplementierungen nutzen können.

Standardmäßige Adressierungssemantik
Im Service Station-Artikel vom Juni 2007 (msdn.microsoft.com/msdnmag/issues/07/06/ServiceStation) erklärte Aaron Skonnard, wie in WCF logische und physische Endpunktadressierung, Adressierungsheader und Nachrichtenfilter verarbeitet werden. In diesem Abschnitt werden einige der grundlegenden Adressierungsfeatures besprochen, und es wird erläutert, wie sie sich auf Routingszenarios auswirken. In Aaron Skonnards Artikel finden Sie auch nützliche zusätzliche Details zu diesen WCF-Features.
In der Regel senden Clients Nachrichten mithilfe eines Proxy, der aus der Dienstbeschreibung generiert wird, direkt an den Zieldienst. Damit Client und Dienst kompatibel sind, nutzen sie gemeinsam gleichwertige Verträge und Endpunktkonfigurationen. Wenn Sie den in Abbildung 1 gezeigten Dienstvertrag und die Konfiguration betrachten, können Sie mehrere wichtige Adressierungsanforderungen für den Dienst ableiten.
                                                            Service Contract
[ServiceContract(Namespace =
"http://www.thatindigogirl.com/samples/2008/01")]
public interface IMessageManagerService
{
  [OperationContract]
  string SendMessage(string msg);

  [OperationContract]
  void SendOneWayMessage(string msg);
}
Endpoint Configuration
<system.serviceModel>
  <services>
    <service name="MessageManager.MessageManagerService"
      behaviorConfiguration="serviceBehavior">
      <endpoint
        address="http://localhost:8000/MessageManagerService"
        contract="MessageManager.IMessageManagerService"
        binding="basicHttpBinding" />
    </service>
  </services>
</system.serviceModel>
Der erwartete Action-Adressierungsheader für eine Anforderung an den SendMessage-Vorgang lautet:
http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService/SendMessage
Da in OperationContractAttribute keine Aktion angegeben wird, wird dieser Wert vom Dienstvertragsnamespace, vom Vertragsnamen (für den standardmäßig der Schnittstellenname verwendet wird) und vom Vorgangsnamen (für den standardmäßig der Methodenname verwendet wird) abgeleitet.
Zweitens lautet der erwartete Action-Header für eine Antwort von SendMessage wie folgt:
http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService/SendMessageResponse
Da mit OperationContractAttribute keine ReplyAction angegeben wird, wird dieser Wert genauso abgeleitet wie die Action-Eigenschaft, wobei das Suffix „Response“ angefügt wird.
Der erwartete To-Header für Nachrichten, die auf den Dienstendpunkt abzielen, lautet wie folgt:
http://localhost:8000/MessageManagerService
Dieser Wert wird vom Adressattribut für das Endpunktelement abgeleitet, das als logische Adresse des Endpunkts angesehen wird. Die Adresse kann zwar anders angegeben werden, doch die physische Adresse des Endpunkts entspricht in der Regel der logischen Adresse. Das bedeutet, dass Clients normalerweise Nachrichten an eine physische Adresse senden, die mit dem To-Header übereinstimmt.
Diese Anforderungen werden mit Dienstmetadaten beschrieben, sodass Clients einen kompatiblen Proxy sowie eine kompatible Konfiguration generieren können. Der Dienstvertrag, der für den Client generiert wird, weist die gleichen Action- und ReplyAction-Einstellungen wie der Dienst auf, und die Clientbindungskonfiguration entspricht einem Endpunkt mit der geeigneten logischen und physischen Adresse. Der folgende Clientendpunkt ist z. B. mit dem Dienst in Abbildung 1 kompatibel:
<client>
  <endpoint 
    address="http://localhost:8000/MessageManagerService"
    binding="basicHttpBinding" 
    contract="localhost.IMessageManagerService" 
    name="basicHttp" />
</client>
Im Clientproxy wird die address-Eigenschaft des Clientendpunktelements sowohl für dessen logische als auch für dessen physische Adresse verwendet. Demzufolge werden Nachrichten an eine physische Adresse gesendet, die dem To-Header entspricht, wie ich weiter oben angedeutet habe. Wenn mithilfe des Proxy der SendMessage-Vorgang aufgerufen wird, wird eine Nachricht mit den To- und Action-Headern gesendet, die in Abbildung 2 gezeigt werden.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1"
      xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">
      http://localhost:8000/MessageManagerService
    </To>
    <Action s:mustUnderstand="1" 
      xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">
      http://www.thatindigogirl.com/samples/2008/01
        /IMessageManagerService/SendMessage
    </Action>
  </s:Header>
  <s:Body>
    <SendMessage xmlns="http://www.thatindigogirl.com/samples/2008/01">
      <msg>test</msg>
    </SendMessage>
  </s:Body>
</s:Envelope>

Die Kombination des To- bzw. Action-Headers zeigt dem Dienstmodell an, welcher Kanalverteiler die Nachricht verarbeiten und welchen Vorgang der Verteiler aufrufen soll. Standardmäßig muss es einen Endpunkt mit einer entsprechenden logischen Adresse für den To-Header sowie einen Vorgang, der dem Action-Header entspricht, geben. Dieser Ablauf ist in Abbildung 3 dargestellt.
Abbildung 3 Typische Adressierung ohne Router (Klicken Sie zum Vergrößern auf das Bild)
In den folgenden Abschnitten werden die Auswirkungen der logischen und der physischen Adresse, der To- und der Action-Header sowie der Regeln zur Nachrichtenfilterung erklärt, wenn ein Router in das Szenario eingeführt wird.

Routingarchitektur
Es gibt einige Variationen für die Entwurfsmöglichkeiten eines Routers. Die meisten Router müssen in der Lage sein, Nachrichten zu empfangen, die sich auf jeden beliebigen Dienst beziehen können, und sie müssen die ursprüngliche Nachricht an den entsprechenden Zieldienst weiterleiten können. Es gibt zwei grundlegende Ansätze zum Entwerfen eines Routers: einen Pass-Through-Router oder einen Verarbeitungsrouter.
Ein Pass-Through-Router ist für den Client transparent. Der Client unterhält Beziehungen mit nachgeschalteten Diensten, aber Nachrichten laufen einfach durch den Router hindurch. Der Client muss die Nachricht mit einem kompatiblen Transportprotokoll und Nachrichtencodierer an den Router senden, aber jegliche Sicherheit, zuverlässige Sitzungen, Anwendungssitzungen oder andere vom Dienstkanal erforderliche Messagingprotokolle werden von der Nachricht erledigt, die im Client generiert wird. Der Router kann sich Nachrichtenheader ansehen und sogar Header einfügen, aber die ursprünglichen Nachrichtenelemente werden immer noch unverändert an den Dienst weitergeleitet. In Abbildung 4 ist diese Beziehung dargestellt.
Abbildung 4 Funktionsweise eines Pass-Through-Routers (Klicken Sie zum Vergrößern auf das Bild)
Ein Verarbeitungsrouter spielt eine aktivere Rolle beim Verarbeiten von Nachrichten für die Anwendung. Der Client hat also eine Beziehung zum Router, was Transport-, Codier- und Protokollkompatibilität betrifft, aber der Client muss zusätzlich immer noch Nachrichten senden können, die mit nachgeschalteten Diensten kompatibel sind. Nachrichten laufen durch den Router zu Diensten, und der Nachrichtentext bleibt, zusammen mit allen erforderlichen Headern für den Dienst, unversehrt.
Sicherheit, zuverlässige Sitzungen und Header oder Nachrichten, die sich auf andere Kommunikationsprotokolle beziehen, werden in der Regel vom Router verarbeitet, und der Router erstellt eine neue Nachricht mit entsprechenden Kommunikationsprotokollen für nachgeschaltete Dienste. In Abbildung 5 ist die Kompatibilität für einen Verarbeitungsrouter dargestellt.
Abbildung 5 Funktionsweise eines Verarbeitungsrouters (Klicken Sie zum Vergrößern auf das Bild)
Für jede Routingkonfiguration gibt es praktische Implementierungen. Es ist auch möglich, eine Hybridlösung zu implementieren, die irgendwo zwischen den beiden liegt.

Der Routervertrag
Router erhalten Nachrichten, die für nachgeschaltete Dienste vorgesehen sind, und sind verantwortlich für das Weiterleiten dieser Nachrichten an den entsprechenden Dienst. Sie sind auch verantwortlich für das Empfangen von Antworten vom Dienst und für das Zurückgeben dieser Antworten an den Client. Mit einem typischen Routervertrag wird ein einzelner Vorgang verfügbar gemacht, der jede Nachrichtenanforderung oder -antwort verarbeiten kann. Im folgenden Beispiel heißt dieser Vorgang „ProcessMessage“:
[ServiceContract(Namespace = 
"http://www.thatindigogirl.com/samples/2008/01")]
public interface IRouterService {
  [OperationContract(Action = "*", ReplyAction = "*")]
  Message ProcessMessage(Message requestMessage);
}
In einer normalen Situation werden die Eigenschaften „Action“ und „ReplyAction“ von OperationContractAttribute wie weiter oben in diesem Artikel erörtert abgeleitet: vom Dienstvertragsnamespace, vom Vertragsnamen und vom Vorgangsnamen. Wenn eine Nachricht eingeht, muss der Kanalverteiler standardmäßig nach einem Vorgang suchen, der dem Action-Header genau entspricht. In einem Fall jedoch, in dem Action und ReplyAction auf „*“ gesetzt sind, werden vom Kanalverteiler alle Nachrichten, die nicht einem spezifischen Vorgang zugeordnet sind, an diesen allumfassenden Vorgang gesendet, ohne Rücksicht auf den Wert des Action-Headers. Zum Vermeiden von Zweideutigkeiten kann nur ein Vorgang „*“ für die Eigenschaft „Action“ bzw. „ReplyAction“ angeben.
Mit einem typischen Router wird ein einzelner Vorgang wie ProcessMessage bereitgestellt, der jede Nachricht verarbeiten kann, die ihn erreicht. Während mit Action und ReplyAction sichergestellt wird, dass die Nachricht durch den Kanalverteiler ProcessMessage zugeordnet wird, muss die Vorgangssignatur ebenfalls jede Nachricht verarbeiten können.
Zu diesem Zweck empfängt ProcessMessage nicht typisierte Nachrichten in Form des Message-Typs und gibt diese zurück. Der Router kann durch diesen Typ auf die Headersammlung und den Nachrichtentext zugreifen, aber über die allgemeinen Adressierungsheader hinaus, die deserialisiert sind und durch stark typisierte Eigenschaften zur Verfügung gestellt werden, findet keine automatische Serialisierung statt.
Jede weitere Verarbeitung der Nachrichten ist Aufgabe der Routerimplementierung. Mit einem grundlegenden Router wird die nicht typisierte Nachricht einfach empfangen und wird so, wie sie ist, an nachgeschaltete Dienste weitergeleitet. Dann wird auf eine Antwort gewartet. Entsprechend wird die Antwort im gleichen Rohformat an den aufrufenden Client weitergeleitet.

Weiterleiten von Nachrichten
Nachdem der Router eine Nachricht empfangen und gemäß seiner eigenen Anforderungen verarbeitet hat, wird die Nachricht zur weiteren Verarbeitung an den entsprechenden nachgeschalteten Dienst weitergeleitet. In Abbildung 6 wird eine einfache Routerimplementierung für den weiter oben erörterten Vertrag gezeigt. Durch ProcessMessage wird mit ChannelFactory<T> ein Clientkanal (oder Proxy) erstellt. Mit diesem Proxy werden Nachrichten an einen bestimmten Dienstendpunkt weitergeleitet, und alle Antworten werden zurückgegeben.
[ServiceBehavior(
  InstanceContextMode = InstanceContextMode.Single, 
  ConcurrencyMode = ConcurrencyMode.Multiple)]
public class RouterService : IRouterService {
  public Message ProcessMessage(Message requestMessage) {
    using (ChannelFactory<IRouterService> factory = 
      new ChannelFactory<IRouterService>("serviceEndpoint")) {

      IRouterService proxy = factory.CreateChannel();

      using (proxy as IDisposable) {
        return proxy.ProcessMessage(requestMessage);
      }
    }
  }
}

Proxys haben meist eine feste Typbindung an den Zieldienstvertrag, aber in diesem Fall sollte der Proxy in der Lage sein, alle Nachrichten weiterzuleiten und alle Antworten zu empfangen, was durch den Routervertrag vereinfacht wird. In diesem einfachen Beispiel wird die ursprüngliche Nachricht lediglich vom Router an den Zieldienst weitergeleitet, und der Router gibt jede Antwort zurück. Wenn der Vorgang am Zieldienst unidirektional ist, wird keine Antwort gesendet.
Da im Vertrag mit nicht typisierten Nachrichten gearbeitet wird, wird die gleiche Nachricht wie in Abbildung 7 gezeigt an den Dienst weitergeleitet. Sie müssen jedoch feststellen, dass eine Änderung an der Nachricht vorgenommen wird, die Sie u. U. nicht erwarten: Der To-Header wird verändert, bevor die Nachricht an den Dienst gesendet wird.
Abbildung 7 Adressierungssemantik durch einen einfachen Router (Klicken Sie zum Vergrößern auf das Bild)
Erinnern Sie sich daran, dass der Proxy standardmäßig die logische Adresse seiner Endpunktkonfiguration verwendet, um den To­Header für ausgehende Nachrichten festzulegen, selbst wenn eine Message-Rohinstanz übergeben wird, die bereits einen To-Header hat. Dies scheint eine gute Sache zu sein, da alle Dienste erfordern, dass der To-Header der logischen Adresse eines ihrer Dienstendpunkte entspricht, doch dies kann Nebenwirkungen verursachen. Wenn z. B. der aktualisierte To-Header nicht signiert ist und wenn für den Dienst Sicherheit aktiviert ist, wird die Nachricht abgelehnt.
Im Idealfall sollte der Client eine Nachricht mit einem To-Header senden, der dem Zieldienst entspricht. Der Router sollte jene Nachricht trotz der Nichtübereinstimmung akzeptieren und sollte diese Nachricht an den Dienst weiterleiten, ohne den To-Header zu verändern. Dies kann durch Bindungskonfigurationen erreicht werden, die als Nächstes erörtert werden sollen.

Logische und physische Adresse
Wenn Sie einen Router in die Anwendungsarchitektur einführen, ist es immer am besten, wenn der Client Nachrichten mit dem richtigen To-Header für den Dienst senden kann, während die Nachricht immer noch an den Router gesendet wird. Eine Möglichkeit, dies zu erreichen, besteht darin, den Client für die Verwendung von ClientViaBehavior zu konfigurieren, wie in der in Abbildung 8 gezeigten Konfiguration. Dadurch wird dem Clientproxy mitgeteilt, dass er eine Nachricht mit einem To-Header generieren soll, die der logischen Adresse des Endpunkts entspricht, und dass die Nachricht aber über die physische Adresse des Routers gesendet werden soll. Das Problem besteht darin, dass der Client dadurch an die Existenz eines Routers gekoppelt wird.
<client>
  <endpoint address="http://localhost:8000/MessageManagerService"
    binding="wsHttpBinding"  
    bindingConfiguration="wsHttpNoSecurity"
    contract="localhost.IMessageManagerService" 
    name="basicHttp" 
    behaviorConfiguration="viaBehavior"/>
</client>
<behaviors>
  <endpointBehaviors>
    <behavior name="viaBehavior">
      <clientVia viaUri="http://localhost:8010/RouterService"/>
    </behavior>
  </endpointBehaviors>
</behaviors>

In einer weiteren Möglichkeit wird der Dienst veranlasst, das listenUri-Attribut für seine Endpunkte zu konfigurieren, damit die logische Adresse des Diensts gleich der des Routers ist, während die physische Adresse für den Dienst spezifisch ist. Betrachten Sie diese Dienstkonfiguration:
<endpoint address="http://localhost:8010/RouterService" 
  contract="MessageManager.IMessageManagerService" 
  binding="wsHttpBinding"
  bindingConfiguration="wsHttpNoSecurity" 
  listenUri="http://localhost:8000/MessageManagerService"/>
Durch die resultierenden Dienstmetadaten wird die Routeradresse für Clients veröffentlicht, sodass Clientendpunkte die Routeradresse widerspiegeln. Ich mag diese Lösung nicht wirklich, da der Dienst an den Router gekoppelt wird, von dem er im Idealfall nichts wissen muss.
Die Alternative wäre, die Dienste eine logische Adresse verwenden zu lassen, bei der es sich um eine Art URI handelt und die nicht an den Router oder an den Dienst gebunden ist. Den Clients wird dann manuell die physische Adresse mitgeteilt, an die die Nachrichten gesendet werden sollen, da diese kein Teil der Metadaten sein wird. Im Folgenden sehen Sie ein Beispiel für diese Art der Endpunktkonfiguration:
<endpoint address="urn:MessageManagerService" 
  contract="MessageManager.IMessageManagerService" 
  binding="wsHttpBinding"  
  bindingConfiguration="wsHttpNoSecurity" 
  listenUri="http://localhost:8000/MessageManagerService"/>
In jedem Fall empfängt der Dienst einen To-Header, der seiner Endpunktkonfiguration entspricht, und der Router empfängt die Nachricht zuerst.
Der Router sollte wirklich die Last der Konfiguration tragen, damit Clients und Dienste nicht von seiner Anwesenheit abhängig sind. Folglich wird der To-Header nie der logischen Adresse des Routers entsprechen. Von den Diensten wird standardmäßig EndpointAddressMessageFilter verwendet, um zu ermitteln, ob der To-Header einer Nachricht einem ihrer konfigurierten Endpunkte entspricht. Da ein Router nicht davon ausgehen kann, dass dies funktioniert, sollte MatchAllMessageFilter installiert werden.
ServiceBehaviorAttribute unterstützt dies durch die Eigenschaft „AddressFilterMode“, die auf eine der AddressFilterMode-Enumerationen gesetzt werden kann: „Exact“ (Standardwert), „Prefix“ oder „Any“. Da nicht garantiert werden kann, dass das Routerpräfix allen Diensten entspricht, für die es Nachrichten empfängt, macht es Sinn, allen To-Headern den Durchlauf zu ermöglichen, etwa so:
[ServiceBehavior(InstanceContextMode = 
  InstanceContextMode.Single, 
  ConcurrencyMode = ConcurrencyMode.Multiple, 
  AddressFilterMode=AddressFilterMode.Any)]
public class RouterService : IRouterService
Standardmäßig wird der To-Header immer aktualisiert, um der logischen Adresse eines Proxys zu entsprechen, basierend auf seiner Endpunktkonfiguration und ohne Rücksicht darauf, ob für die To-Adresse bereits der richtige Wert festgelegt ist. Um dieses Verhalten zu unterdrücken, sodass der Router dem Dienst Nachrichten mit dem originalen To-Header weiterleiten kann, muss der Router eine Bindungskonfiguration mit manueller Adressierung verwenden. Dies ist keine Eigenschaft, die für eine der Standardbindungen festgelegt werden kann. Deshalb müssen Sie benutzerdefinierte Bindung verwenden, um dies zu erreichen.
In dem folgenden Codeausschnitt wird ein customBinding-Abschnitt dargestellt, mit dem dieses Feature für den HTTP-Transportkanal festgelegt wird:
<customBinding>
  <binding name="manualAddressing">
    <textMessageEncoding />
    <httpTransport manualAddressing="true"/>
  </binding>
 </customBinding>
Dies vereinfacht den in Abbildung 9 gezeigten Adressierungsfluss, in dem Header nicht geändert werden.
Abbildung 9 Adressierungssemantik durch einen Router mit manueller Adressierung (Klicken Sie zum Vergrößern auf das Bild)

MustUnderstand-Header
Bisher habe ich mich auf eine einfache Routingimplementierung konzentriert, um wichtige Überlegungen beim Routerentwurf zu veranschaulichen, die sich auch auf Adressierung, Filterung und Bindungskonfiguration auswirken. Diese einfache Routinglösung funktioniert nur, wenn der Dienst keine Sicherheit, keine zuverlässige Sitzungen und kein anderes umfassendes Protokoll für seine Bindungen aktiviert. In Abbildung 10 wird eine vereinfachte Ansicht der Bindungsprotokolle gezeigt, die ich für die Erörterung bis jetzt vorausgesetzt habe.
Abbildung 10 Dienstvertrag und Endpunktkonfiguration (Klicken Sie zum Vergrößern auf das Bild)
In Abbildung 11 wird die gleiche Ansicht für einen Pass-Through-Router angezeigt, wenn der Dienst Sicherheit und zuverlässige Sitzungen erfordert. Das Aktivieren dieser Protokolle bedeutet, dass die Client- und Dienstkanäle zusätzliche Nachrichten austauschen, um Sitzungen einzurichten, Sicherheitstoken anzufordern und weiteres, ähnlich geartetes Messaging durchzuführen. Da der Router allen Nachrichten ermöglicht durchzulaufen, werden diese protokollspezifischen Nachrichten auch an den Dienst übertragen, und das ist gut so.
Abbildung 11 Pass-Through-Konfiguration mit Sicherheit und zuverlässigen Sitzungen (Klicken Sie zum Vergrößern auf das Bild)
Es entsteht jedoch ein Problem, wenn die Nachrichten an und von dem Dienst Header enthalten, die von ihrem Empfänger verstanden werden müssen. Da im Pass-Through-Router absichtlich keine Sicherheit und keine zuverlässigen Sitzungen aktiviert sind, sind diese Kanäle nicht vorhanden, um zugeordnete Protokollheader zu verarbeiten.
Sie können den Routerdienst anweisen, MustUnderstand-Header zu ignorieren, indem Sie die ValidateMustUnderstand-Eigenschaft von ServiceBehaviorAttribute wie hier gezeigt auf „false“ setzen:
[ServiceBehavior(InstanceContextMode = 
  InstanceContextMode.Single, 
  ConcurrencyMode = ConcurrencyMode.Multiple, 
  AddressFilterMode=AddressFilterMode.Any, 
  ValidateMustUnderstand=false)]
public class RouterService : IRouterService
Dadurch werden eingehende Nachrichten vom Client adressiert, aber es werden keine Nachrichten adressiert, die von nachgeschalteten Diensten zurückgegeben werden.
Deshalb müssen Sie auch die Routerimplementierung ändern, um dieses Verhalten beim Initialisieren der Kanalfactory zum Aufrufen des nachgeschalteten Diensts anzugeben, etwa so:
using (ChannelFactory<IRouterService> factory = 
  new ChannelFactory<IRouterService>("serviceEndpoint"))
{
  factory.Endpoint.Behaviors.Add(new MustUnderstandBehavior(false));
  IRouterService proxy = factory.CreateChannel();
  
  // remaining code
}
Jetzt können Protokoll- und Dienstnachrichten frei durch den Router zwischen Client und Dienst hin und her fließen, vorausgesetzt, das verwendete Protokoll ist HTTP.
Eine weitere Komplikation entsteht, wenn Duplexprotokolle wie TCP oder Named Pipes eingesetzt werden. Das bedeutet, dass Nachrichten vom Dienst an den Client initiiert werden können, z. B. wenn zuverlässige Sitzungen aktiviert sind. Es gibt eine erweiterte Routerkonfiguration, mit der Sie diesen Sonderfall behandeln können, aber auf dieses Szenario und seine praktische Anwendbarkeit wird in Teil 2 dieser Reihe eingegangen.

Senden Sie Ihre Fragen und Kommentare (in englischer Sprache) an sstation@microsoft.com.


Michele Leroux Bustamante ist leitende Architektin bei IDesign Inc., Microsoft Regional Director für San Diego und Microsoft-MVP für verbundene Systeme. Ihr aktuelles Buch ist Learning WCF. Sie erreichen sie unter „mlb@idesign.net“, oder besuchen Sie design.net. Michele Leroux Bustamante führt einen Blog unter dasblonde.net.

Page view tracker