Hosting und Nutzung von Services

Veröffentlicht: 20. Apr 2006

Von Christian Weyer

Die Windows Communication Foundation (WCF) vereinheitlicht und vereinfacht das Modellieren verteilter Anwendungen. Die Services sollten unabhängig von Kontrakt, Bindung und Adresse zur Nutzung angeboten werden. Dieser Beitrag erläutert die Hosting-Möglichkeiten für WCFServices und den Zugriff von Clients auf die Services.

 

dotnetpro

Diesen Artikel können Sie dank freundlicher Unterstützung von dotnetpro auf MSDN Online lesen. dotnetpro ist ein Partner von MSDN Online.
Ausgabe 1/2006

Dieser Beitrag stammt aus einer Artikelserie über die Windows Communication Foundation (WCF), erschienen in der dotnetpro.

 


Zur Partnerübersichtsseite von MSDN Online

Auf dieser Seite

Wer die Wahl hat ... Wer die Wahl hat ...
Immer das Gleiche Immer das Gleiche
Ich will reden! Ich will reden!
Fazit Fazit
Der Autor Der Autor

In den vorangegangenen Beiträgen zur Windows Communication Foundation (WCF) in der dotnetpro wurden die Basiskonzepte von Kommunikationsendpunkten beleuchtet. Jetzt ist es an der Zeit, die modellierten und implementierten Services anderen auch zur Nutzung anzubieten.

Da Services immer in Form von DLLAssemblies erstellt werden, benötigt man einen Host Container, in den diese Assemblies geladen werden können. Dies ist in etwa vergleichbar mit der Situation bei einer .NET-Remoting-Applikation. Auch hier muss sich der Entwickler entscheiden, in welcher Umgebung die Remoting-Komponenten gehostet werden sollen. Der Entwickler sieht sich dabei einer Reihe von Möglichkeiten gegenüber, die in Tabelle 1 aufgelistet sind.

Wer die Wahl hat ...

Beim Blick auf Tabelle 1 werden dem Remoting- Entwickler altbekannte Gefühle wieder aufstoßen. Welchen Aufwand für die Programmierung kann ich in Kauf nehmen? HTTP als Transportprotokoll ist nicht ausreichend, was tun? Diese und viele ähnliche Fragen scheinen nun wieder auf der Tagesordnung zu stehen.

Tabelle 1: Hosting-Möglichkeiten für WCF-Services.

Hosting Container

Beschreibung

Konsolenanwendung

Selbst erstellte Anwendung; muss gestartet werden; läuft als normaler Prozess; kann über alle Protokolle aktiviert werden

Windows-Forms-Anwendung

Selbst erstellte Anwendung; muss gestartet werden; läuft als normaler Prozess; kann über alle Protokolle aktiviert werden

Windows-Dienst

Selbst erstellte Anwendung; kann automatisch gestartet werden; läuft im Hintergrund unter konfigurierbarem Account; kann über alle Protokolle aktiviert werden

COM+

Selbst erstellte Rahmenanwendung; kann automatisch gestartet werden; kann im Hintergrund unter konfigurierbarem Account laufen; konfigurierbares, robustes und erprobtes Prozessmodell; kann nur über DCOM aktiviert werden

IIS/ASP.NET

Konfigurierbares, bewährtes und robustes Prozessmodell; kann nur über HTTP aktiviert werden

Windows Activation Service (WAS)

Es würde den Rahmen dieses Artikels sprengen, alle Vor- und Nachteile der jeweiligen Prozess- und Hosting-Modelle zu diskutieren. Fakt ist aber, dass es derzeit kein wirklich optimales Hosting-Modell gibt. Entweder muss man vieles immer wieder selbst machen oder man hat vieles bereits vorgefertigt, muss aber Einschränkungen hinnehmen, etwa bei der Unterstützung von Transportprotokollen. Wenn Sie zu den glücklichen Lesern gehören, die ab 2006 auf Windows Vista und ab 2007 auf Windows, Codename Longhorn Server, setzen können, dann sind Sie aus dem Schneider. Dort wird es den Windows Activation Service (WAS) geben. In einem Satz gesagt, ist der WAS ein neuer und eigener NT-Dienst, in dem alle guten Eigenschaften des IIS in Bezug auf Hosting und Aktivierung zusammengefasst wurden.

Der Windows Activation Service (WAS)

Der Windows Activation Service (WAS) wird nur für Windows Vista und Windows, Code name Longhorn Server, bereitgestellt. Dies hat zur Konsequenz, dass WCF-Anwendungen unter Windows XP SP2 und Windows Server 2003 nur über HTTP aktiviert werden können, wenn man sich für ein generisch bereitgestelltes Hosting-Modell über ASP.NET/IIS entscheidet. Alternativ kann man auch eigene Host-Anwendungen schreiben.

Im Folgenden steht exemplarisch das Hosten eines WCF Services in einer simplen Windows-Forms-Anwendung und alternativ die Bereitstellung über ASP.NET und den IIS an.

Bei den Optionen Konsolenanwendung, Windows-Forms-Anwendung und NT- Dienst muss der Entwickler sich selbst um das Bereitstellen der Services kümmern. Die WCF-Laufzeitumgebung bietet hier jedoch Unterstützung an. Diese Unterstützung befindet sich, wie fast alles bei WCF, in der Assembly System.Service-Model.dll. Zentrales Objekt der Begierde bei der Bereitstellung von Services ist die Klasse ServiceHost. Über den ServiceHost spezifiziert man, welche Services anderen tatsächlich über den Host-Prozess angeboten werden sollen.

Listing 1 zeigt eine Windows Form, in der interessierten Konsumenten auf einen Button-Klick hin der bereits in früheren Artikeln vorgestellte und entwickelte Konferenz-Service angeboten wird. Ein Blick auf die Projektstruktur in Visual Studio 2005 (siehe Abbildung 1) zeigt, dass das Host-Projekt einen Assembly-Verweis auf die Kontraktdaten und den eigentlichen Service besitzt. Dies ist notwendig, denn der ServiceHost stellt einen ganz bestimmten Service-Typ bereit und ein Service-Typ kann eine oder mehrere Schnittstellen (Kontrakte) implementieren. Der Konstruktor von ServiceHost kann entweder eine Typ-Information oder aber ein bereits existierendes Service-Objekt entgegen nehmen. In jedem Fall muss der Entwickler beim Anlegen der ServiceHost-Instanz eine Basisadresse spezifizieren. Diese Basisadresse wird verwendet, um die notwendigen Metadaten in Form von XSDs, WSDLs, Policies und von WS-MetadataExchange-konformen Anfragen zur Verfügung zu stellen.

Listing 1: Hosting in einer Windows-Forms-Anwendung.

public partial class MainForm : Form
{
   private ServiceHost sh;
   public MainForm()
   {
      InitializeComponent();
   }
   private void bnStart_Click(object sender, EventArgs e)
   {
      sh = new ServiceHost(typeof(ConferenceServiceImplementation), new Uri(“http://localhost:7777/Conference/“));
      sh.Open();
   }
   private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
   {
      if(sh.State == CommunicationState.Opened)
         sh.Close();
      sh.Dispose();
   }
}

Die Projektstruktur der Beispielapplikation
Abbildung 1 Die Projektstruktur der Beispielapplikation.

Wird, wie im Beispiel hier, eine HTTPAdresse angegeben, heißt dies nicht, dass der Service unter eben dieser Adresse zu erreichen ist. Tatsächlich ist der Service zu dieser Zeit überhaupt noch nicht zu erreichen, denn es gibt noch keinerlei Informationen, wie mit dem Service kommuniziert werden soll, es wurde schließlich noch kein Binding festgelegt. Wie in der vorangegangenen Ausgabe [1] bereits gezeigt, werden Bindings deklarativ in einer Konfigurationsdatei festgelegt.

Damit der Konferenz-Service auch tatsächlich funktionieren kann, sind noch die Informationen zum Betrieb erforderlich. Eine exemplarische Konfiguration zeigt Listing 2. Der Konferenz-Service bekommt insgesamt vier Endpunkte, über die drei unterschiedliche Kontrakte veröffentlicht werden.

Listing 2: Konfiguration für das Hosten eines WCF Services mit mehreren Endpunkten.

<system.serviceModel>
  <services>
    <service type=“Service.ConferenceServiceImplementation, Service“>
      <endpoint
         address=“http://localhost:7777/Conference/Service“
         binding=“basicHttpBinding“
         contract=“Contracts.IConferenceService, Contracts“ />
      <endpoint
         address=“net.tcp://localhost:7778/Conference/“
         binding=“netTcpBinding“
         contract=“Contracts.IConferenceService, Contracts“ />
      <endpoint
         address=“http://localhost:7777/Conference/Duplex“
         binding=“wsDualHttpBinding“
         contract=“Contracts.IConferenceDuplexService, Contracts“ />
      <endpoint
         address=“net.tcp://localhost:8090/Conference/Streaming“
         binding=“customBinding“
         contract=“Contracts.IConferenceStreamingService, Contracts“
         bindingConfiguration=“tcpStreaming“ />
  </service>
</services>

<bindings>
  <customBinding>
    <binding name=“tcpStreaming“>
      <compositeDuplex />
      <binaryMessageEncoding />
      <tcpTransport transferMode=“Streamed“
        maxMessageSize=“70000000“ />
    </binding>
  </customBinding>
 </bindings>
</system.serviceModel>

Die Schnittstelle IConferenceService wird sowohl über ein BasicHttpBinding als auch über ein NetTcpBinding angeboten. Dies ermöglicht die Kommunikation mit dem Kern des Konferenzdienstes über interoperable Web-Services-Technologien (per BasicHttpBinding) und auch die optimierte Variante mit binärer Codierung und aktivierter Sicherheit über TCP (per NetTcpBinding). Zusätzlich eröffnet die Konfiguration auch eine Duplex-Konversation über das WsDualHttpBinding. Zu guter Letzt wird ein weiteres TCP- basiertes Binding definiert. Dieses custom Binding legt fest, dass die Kommunikation zwischen dem Client und dem Service über Streams erfolgen soll. Für die Beispiel applikation hat dies zur Folge, dass beispielsweise eine Operation zur Auflistung aller verfügbaren Konferenzvorträge nur über TCP aufgerufen werden kann und die resultierenden Daten als Stream übertragen werden. Mit den wenigen relevanten Zeilen aus Listing 1 und der Konfiguration aus Listing 2 ist der Service nun fertig und lauffähig. Es sei noch angemerkt, dass es für die Erstellung und Manipulation der XML-basierten Konfigurationsdateien eine grafische Oberfläche geben wird. Wenn die Windows-Forms-Anwendung gestartet ist, wie in Abbildung 2 zu sehen, und der Service nach einem Klick bereitgestellt ist, kann man über jeden handelsüblichen Browser zu einer dynamisch erzeugten Dokumentationsseite navigieren – wie in Abbildung 5 zu sehen. Und wie weiter oben erläutert, befindet sich diese Testseite an dem URL, der für die Basisadresse beim Anlegen der ServiceHost-Instanz festgelegt wurde. Für das sorgfältige Beenden des Hostings sind ein paar wichtige Punkte zu beachten. Beim Schließen des Formulars wird die ServiceHost-Instanz geschlossen und die Ressourcen mittels Dispose bereinigt. Eine äquivalente Vorgehensweise ist notwendig, um WCF Services in einem NT-Dienst zu hosten. Der Code und die Konfiguration können gleich bleiben. Die Codefragmente müssen nur in die OnStart- und OnStop-Methoden eines NTDienstes implantiert werden.

Eigener WCF Service Host in Aktion
Abbildung 2 Eigener WCF Service Host in Aktion.

Immer das Gleiche

Auf dem Weg hin zu einer robusten, skalierbaren und sicheren Hosting-Umgebung kann man sich auf Altbewährtes verlassen. WCF Services lassen sich nahezu ohne Probleme auch in ASP.NET hosten. Da in vermutlich 95 Prozent aller Fälle ASP.NET im IIS verwendet wird, kann man über diese Hosting-Möglichkeit wichtige Funktionen des IIS einfach mitbenutzen, zum Beispiel Sicherheit, Prozessmodell, Konfiguration, Logging und Monitoring.

Ein erneuter Blick auf Abbildung 1 zeigt, dass auch bei diesem Ansatz Assembly-Verweise auf die Service- und die Contracts-DLLs notwendig sind. Mit einer Herangehensweise analog zum Windows-Forms-Projekt oben lässt sich der Service über ASP.NET/IIS mit wesentlich weniger Aufwand bereitstellen. Hierzu ist lediglich eine Verankerungsdatei erforderlich, ähnlich wie bei ASMX-basierten Web Services. Die Erweiterung für WCF Services lautet svc. Eine svc-Datei beinhaltet dann eine Verankerungsanweisung für ASP.NET:

<%@Service language=C# class=“Service.ConferenceServiceImplementation, Service“  %>

Konsequent betrachtet wäre die svc-Datei gar nicht notwendig. Ihre Existenz begründet sich vor allem dadurch, dass man auf dieser Datei im NTFS-Dateisystem Berechtigungen setzen kann. Denn die Abbildung der svc-Erweiterung in die ASP.NET-Laufzeitumgebung geschieht eigentlich über die ISAPI-Erweiterung aspnet_isapi.dll. Eine neue Version dieser ASP.NET-Programmierern bekannten DLL wird durch die WCF-Installationsroutine installiert und für das Mapping von svc-Dateien im IIS registriert – Abbildung 3.

Konfiguration für die svc-Dateierweiterung im IIS
Abbildung 3 Konfiguration für die svc-Dateierweiterung im IIS.

Um die vollständige Architektur des Hostings von WCF Services über den IIS und ASP.NET zu verstehen, hilft ein Blick auf Abbildung 4 weiter. In diesem Schaubild ist das Zusammenspiel des IIS 6 mit WCF ersichtlich. Ähnlich wie bei ASMX und dem IIS 6, ist der zentrale Kommunikationspunkt der Kernel-Level-Treiber http.sys. Hier werden HTTP-Anfragen entgegengenommen und an den zentralen W3SVC-Dienst weitergeleitet. Dieser schaut in der Konfigurationsdatenbank IIS Metabase nach, wie die angeforderte Applikation physikalisch bereitgestellt wird. Hierzu gehören zum Beispiel der zu verwendende Application Pool und diverse Sicherheitseinstellungen. Schlussendlich wird entweder ein neuer Arbeiterprozess (w3wp.exe) gestartet oder ein bereits existierender, mit den Konfigurationsanforderungen kompatibler Arbeiterprozess kontaktiert. Fortan erfolgt jegliche Kommunikation direkt zwischen http.sys und dem entsprechenden w3wp.exe-Arbeiterprozess.

Architektur des Hostings über ASP.NET und IIS 6
Abbildung 4 Architektur des Hostings über ASP.NET und IIS 6.

Damit der Konferenz-Service im IIS auch wirklich funktioniert, fehlt noch eine passende Konfiguration in Form einer web.config-Datei. Zu den bereits erwähnten Einschränkungen des Hostings im IIS gehört, dass WCF Services nur über http aktiviert werden können. Daher kommen auch nur das BasicHttpBinding, das WsHttpBinding oder das WsDualHttpBinding in Frage. Listing 3 stellt den Konferenzdienst der Außenwelt in Form der IConferenceService-Schnittstelle über ein BasicHttpBinding zur Verfügung.

Listing 3: Mögliche Konfiguration für das Hosten über IIS/ASP.NET.

<system.serviceModel>
  <services>
    <service type=“Service.ConferenceServiceImplementation, Service“>
      <endpoint
        address=““
        binding=“basicHttpBinding“
        contract=“Contracts.I
        ConferenceService, Contracts“ />
    </service>
  </services>
</system.serviceModel>

Ist der WCF Service erfolgreich im IIS gehostet, wird man wie beim custom Hosting mit einer automatisch generierten Dokumentationsseite belohnt. Abbildung 5 zeigt diese Seite im Browser zusammen mit dem gestarteten Test- und Entwicklungs-Webserver von Visual Studio 2005, denn der Konferenz-Service wird über diesen Test-Webserver bereitgestellt, der aber auch ASP.NET unterstützt. Somit sind wir gerüstet für das Hosten von WCF Services. Doch was ist mit dem Konsumieren dieser Services?

WCF-Service-Dokumentationsseite
Abbildung 5 WCF-Service-Dokumentationsseite.

Ich will reden!

Zu einer erfolgreichen Kommunikation gehören bekanntlich mindestens zwei Parteien. Oben wurde die eine Partei, der Service, erfolgreich in Szene gesetzt, bleibt nun, sich um die Clients zu kümmern. WCF-Client-Applikationen haben prinzipiell zwei Möglichkeiten eine erfolgreiche Kommunikation mit Services aufzunehmen. Immer mit im Spiel ist das bekannte Proxy-Pattern. Entweder wird eine Proxy-Klasse explizit aus den interoperablen Metadaten oder aber implizit über einen Factory-basierten Ansatz mit reinen .NET Interfaces erzeugt.

Die einfachste und wohl auch sauberste Art der Kommunikation mit Services erfolgt über eine Factory. In der WCFLaufzeitumgebung gibt es Hilfsklassen, die es ermöglichen, nur den Schnittstellenbeschreibungen und dazu passenden Konfigurationsdateien einen benötigten Kommunikationskanal (Channel) zum Service zu etablieren. Denn eine Client-Anwendung darf nur Metadaten eines Services kennen und nicht mehr. Und mit der Factory kann man beispielsweise in einer reinen WCF-Landschaft einfach dem Client-Projekt einen Verweis auf die Contracts-Assembly hinzufügen, wie in Abbildung 6 gezeigt.

Client-Anwendung mit Verweis auf die Metadaten-Assembly
Abbildung 6 Client-Anwendung mit Verweis auf die Metadaten-Assembly.

Mit der richtigen Konfiguration unter dem Arm, siehe Listing 4, lässt sich somit eine erfolgreiche Kommunikation mit dem Service wie folgt erreichen:

private ChannelFactory<Contracts.I
ConferenceService> confFac =
  new ChannelFactory<Contracts.I
ConferenceService>(“conf“);
private Contracts.IonferenceService confChannel;
confChannel = confFac.CreateChannel();
Contracts.SessionRequest request =
  new Contracts.SessionRequest();
request.SessionId = Convert.ToInt32(tbSessionID.Text);
Contracts.SessionResponse result =
  confChannel.RetrieveSessionDetails(request);
pgSession.SelectedObject = result.Session;

Basierend auf der Schnittstellendefinition reicht die Factory eine gültige Instanz eines dynamisch erzeugten Proxys an den aufrufenden Code. Sodann ist ein Aufruf über ein nachrichtenorientiertes Programmiermodell einfach und eingängig, wie oben zu sehen. Zu erwähnen ist jedoch noch, dass für den Factory-Ansatz immer auf eine ganz spezielle Konfiguration hingewiesen werden muss. Dies geschieht über den Konstruktor von ChannelFactory, indem etwa eine Zeichenkette übergeben wird, die auf einen Konfigurationsnamen in der app.config oder web.config verweist. Alternativ lässt sich der tatsächliche Proxy-Code auch zur Entwicklungszeit aus den XML-basierten Metadaten eines Services erzeugen und ins Projekt einfügen. Ähnlich wie bei ASMX und dem Hilfswerkzeug wsdl.exe kann auch bei WCF aus WSDL und XSD.NET-Code generiert werden. Die folgende Anweisung bedient sich daher des WCF-eigenen Tools svcutil.exe und erzeugt eine C#-Proxy-Datei aus den Metadaten eines Services. Dieser Service könnte mit einer beliebigen Programmiersprache auf einer beliebigen Plattform erstellt worden sein – das Einzige, was interessiert, sind die interoperablen Metadaten.

svcutil /out:ConferenceServiceProxy.cs
  http://localhost:7777/Conference/?wsdl

Die aus diesem Aufruf resultierende Proxy-Datei verwendet ein Entwickler dann genauso wie auch schon zu ASMXZeiten. Und ebenso wie bei der Factory-Methode gilt es auf die zu verwendende Konfiguration, also den Kommunikationsendpunkt, hinzuweisen, der in der Konfigurationsdatei definiert wurde. Diese Konfigurationsdatei wurde ebenfalls durch das svcutil.exe-Werkzeug generiert. Denn alle notwendigen Informationen zur Kommunikation befinden sich in Form von Policy-Beschreibungen in den Metadaten des Services.

private ConferenceServiceProxy proxy;
proxy = new ConferenceServiceProxy(“confProxy“);

Im Falle des erzeugten Proxys benötigt das Client-Projekt den physikalischen Verweis auf die Contracts-Assembly nicht mehr. Zumal man diesen Ansatz besonders dann wählt, wenn man die Services nicht kennt und nicht unter Kontrolle oder gar in Eigenentwicklung hat.

Voilà, so einfach kann die Arbeit mit WCF sein, auch für Clients und Konsumenten von Services.

Der gestartete Client kann dann entweder über eine Factory oder den generierten Proxy mit dem Service Verbindung aufnehmen.

Fazit

Mit der Windows Communication Foundation bieten sich dem Entwickler mehrere Möglichkeiten der Bereitstellung von Services. Ob eigenhändig erstellt oder aber im bewährten IIS mithilfe von ASP.NET.

Auf der anderen Seite lassen sich Services, ob nun mit WCF erstellt oder nicht, sehr einfach und komfortabel konsumieren. Denn am Ende des Tages kommt es hier nur auf eines an: die Metadaten für die Schnittstellenbeschreibung. Und wie man diese einbindet, ist entweder persönlicher Gusto oder aber von der Projektsituation abhängig.

[1] Christian Weyer, Windows Communication Foundation, Bindings für Services, dotnetpro 12/2005, Seite 136 ff.

[2] WinFX SDK auf MSDN, mit WCF als Bestandteil, winfx.msdn.microsoft.com/library

Der Autor

Christian Weyer ist Mitbegründer von thinktecture, einer Firma, die Softwarearchitekten und Entwickler bei der Verwendung von .NET- und Web-Service-Technologien unterstützt. Er ist Mitglied im Indigo-Digerati-Team, das sehr frühen Zugriff auf WCF/Indigo Builds eingeräumt bekommt und Feedback an das Entwicklungsteam liefert. Sie erreichen ihn über www.thinktecture.com/staff/christian.