Dieser Artikel wurde maschinell übersetzt.

Webdienste auf Contract-First-Basis

Schemabasierte Entwicklung mit Windows Communication Foundation

Christian Weyer

"Wenn ich acht Stunden zum Fällen eines Baums hätte, würde ich sechs mit dem Schärfen der Axt verbringen" sagte Abraham Lincoln. Dies gilt ebenso für die verschiedenen Phasen in der Softwareentwicklung. Ein gut durchdachter Entwurf ist das Geheimnis hinter den meisten Software-Erfolgsgeschichten. Dies gilt vor allem für den Entwurf von Kommunikationssystemen wie Web Services (WS). Besondere Aufmerksamkeit sollte der formalen Schnittstelle für die Kommunikation geschenkt werden. Diese Schnittstelle bestimmt die Verwendbarkeit und Interoperabilität Ihres Systems. Folglich ist der Entwurf dieser Schnittstelle, der auch als der Vertrag bezeichnet wird, während der frühen Phasen des Lebenszyklus wichtig. In diesem Artikel zeigen wir Ihnen, wie Sie Verträge zuerst für Ihre Windows Communication Foundation (WCF)-basierten Dienste entwerfen und entwickeln, mit Fokus auf den Bereich der Webdienste. Mit WS-Verträgen stehen zwei wichtige Methoden zur Verfügung: Code-First Contract-First-Entwicklung und Schema-First Contract-First-Entwicklung. Unsere Fokus gilt hauptsächlich letzterem.

Was ist die Contract-First-Entwicklung?

Contract-First-Entwurf und -Entwicklung ist nichts Neues. Es wurde formell von Bertrand Meyer als Teil seiner Eiffel-Programmiersprache eingeführt und ist seit 1986 12 in verschiedenen technischen Publikationen erschienen. Daher kann das Verständnis von Verträgen aus Sicht der Tools/Technologien der alten Schule hilfreich sein, um zu erfassen, warum es nützlich ist.

Obwohl Computer heute Vieles mit einfacher Arithmetik zur Steuerung der Satelliten um unsere Erde tun können, hat sich die grundlegende Idee eines Input-Output-Computers seit der Erfindung von Rechenmaschinen im 19. Jahrhundert nicht geändert. Folglich schreiben Softwareentwickler weiterhin Funktionen, die Eingaben benötigen, und führen einige Arbeiten aus, die zu einer Ausgabe führen. Diese Funktionen werden dann von anderen verwendet. Ein Vertrag einer Funktion definiert die Erwartungen und Verpflichtungen dieser Funktion. Mit anderen Worten, Eingabeparameter an eine Funktion können als ihre Erwartungen betrachtet werden und die Rückgabewerte als ihre Verpflichtung. Die Benutzer der Funktion müssen nur den Vertrag kennen, um ihn zu verwenden. Ein klassisches Beispiel dafür ist eine C++-Headerdatei. Wenn Sie eine Funktion in einer anderen C++-Bibliothek aufrufen möchten, sind Sie nicht an der Implementierung interessiert. Die meiste Zeit hätten Sie gar keinen Zugriff auf die Implementierung. Die Nur-Text-Headerdatei informiert Sie und den Compiler genug über das, was Sie für den Aufruf der Funktion benötigen und was nach Abschluss ausgegeben wird.

Typbibliotheksdateien in COM und ActiveX sowie Schnittstellen in C# werden häufig als andere Arten von Verträgen betrachtet. Da ein Vertrag eine Möglichkeit zum Definieren von formalen, genauen und überprüfbaren Schnittstellenspezifikationen basierend auf abstrakten Datentypen für Softwarekomponenten ist, besteht eine natürliche Tendenz, die Verträge zuerst zu erstellen. Dies ist der Grund, warum fast jeder C/C++-Programmierer ein Programm dadurch beginnt, dass er zuerst die Header-Datei schreibt.

Verträge in WS stellen keine Ausnahme dar. Als WS-Entwickler besteht Ihre Absicht darin, das Input-/Output-System als Webdienst freizugeben, möglicherweise über Plattformgrenzen hinweg. Wir sind sicher, dass Sie schon mehrmals zuvor gehört haben, dass sich die Benutzer Ihres Webdiensts keine Sorgen über die Implementierungsdetails zu machen brauchen. Der Zugang zu Ihrem Vertrag sollte für jemand anders ausreichen, ihn zu verwenden. Da Webdienste plattformunabhängig sind, verwenden wir interoperable, standardbasierte Konstrukte wie WSDL (Web Service Definition Language) und XML-Schema (XSD), um die Verträge zu definieren. Das Modellieren eines Vertrags umfasst das Modellieren von Daten, Nachrichten und der Schnittstelle Ihres Webdienstes.

Zunächst modellieren Sie Ihre Datenverträge (d. h. Datenstrukturen), die verwendet werden, um Daten zwischen Clients und dem Dienst zu übergeben. Datenverträge könnten entweder einfache oder primitive Typen wie z. B. Zeichenfolge, Ganzzahl, doppelt oder komplex oder domänenspezifische Typen wie Product, Employee oder Itinerary sein.

Der Stil RPC-Encoded verwendet im Schema definierte Typen, um die Teile der WSDL-Nachrichten zu definieren. Dies erschwert die Überprüfung des gesamten SOAP-Textkörpers gegen das Schema, da nur ein Teil davon das darstellt, was im Schema definiert ist. Der Rest stammt aus der WSDL. Er codiert auch den SOAP-Textkörperinhalt gemäß Abschnitt 5 der durch die SOAP 1.1-Spezifikation definierten Codierungsregeln. Obwohl dieses Format zulässig ist, ist es nicht WS-I-kompatibel.

Das RPC-Literal-Format ist sehr ähnlich wie das RPC/Encoded-Format. Jedoch codiert es im Gegensatz zum RPC/Encoded-Stil nicht den SOAP-Textkörper. Obwohl dieses Format WS-I-kompatibel ist, ist es schwer überprüfbar.

Document/Literal verwendet Elemente, um Teile der WSDL-Nachrichten zu definieren, und codiert den Textinhalt nicht. Dadurch werden beide Hauptprobleme in den zuvor erläuterten Formaten beseitigt. Daher ist dies zum weitgehend akzeptierten WS-I-kompatiblen Format geworden.

Das Format Document/Literal/Wrapped wurde von Microsoft eingeführt. Es entspricht dem Stil Document/Literal, aber es umfasst auch den Inhalt des Elements innerhalb eines durch den Namen des Vorgangs erstellten Elements. Wenn der Vorgangsname im Textkörper enthalten ist, können Nachrichten von einem Nicht-HTTP-Endpunkt aus leichter gesendet werden. Aber noch wichtiger ist jedoch, dass es hilfreich ist, mit dem WS-I-Standard kompatibel zu sein, wofür nur ein untergeordnetes Element innerhalb des Elements erforderlich ist.

In der Regel interagieren die Webdienste mir ihren Clients durch den Austausch von SOAP-Nachrichten 3. Die Modellierung dieser Nachrichtenverträge ist der zweite Schritt der Contract-first-Entwicklung. Wie Sie dies tun, hängt davon ab, welches SOAP-Nachrichtenformat Sie verwenden möchten:

  • RPC/Encoded
  • RPC/Literal
  • Document/Literal
  • Document/Literal/Wrapped

Es gibt auch ein anderes Format namens Document/Encoded, aber es ist äußerst schwer, Implementierungen für seine Verwendung zu finden. Für diesen Artikel legen wir den Fokus nur auf Document/Literal/Wrapped, da es das am häufigsten verwendete Format ist und es auch WS-I-kompatibel (Web Services Interoperability Organization) ist.

Das Definieren eines Nachrichtenvertrags hat zwei Aspekte. Zunächst sollte die Struktur des SOAP-Textkörpers definiert werden. Dazu können Sie XSD verwenden. Außerdem können Sie auch Datenverträge verwenden, die Sie im vorherigen Schritt definiert haben. Der andere Aspekt des Nachrichtenvertrags besteht im Definieren der Struktur des SOAP-Headers. Die Kopfzeilen für Ihre Nachrichten werden in WSDL definiert. Es ist üblich, Datenverträge zu verwenden, die in Schritt 1 modelliert wurden, um zu definieren, was in diesen Headern aufgenommen werden soll.

Nachdem Sie Ihre Daten und Nachrichtenverträge haben, können Sie Ihre Schnittstelle modellieren, indem Sie eine oder mehrere Vorgänge im Dienst definieren. Ein Vorgangsvertrag kann Nachrichtenverträge verwenden, die im zweiten Schritt modelliert wurden, um zu definieren, welche Nachrichten während dieses Vorgangs ausgetauscht werden.

Zusätzlich zu den drei primären Vertragstypen (Daten, Nachricht und Schnittstelle) verfügt ein Webdienstvertrag auch über eine Richtlinie, Bindungen und Endpunkte. Abbildung 1 fasst zusammen, welche WSDL-/Schema-Konstrukte für die Darstellung verschiedener Artefakte in Webdienstverträgen verwendet werden.


Abbildung 1 Verwendung von WSDL- und XSD-Konstrukten

Code-First im Vergleich zu Schema-First

Wie in der der Einführung erwähnt, stehen zwei Möglichkeiten für die Modellierung Ihrer Verträge zur Verfügung: Code-First und Schema-First. Es ist wichtig, beide zu verstehen, um die Option auszuwählen, die Ihren Anforderungen entspricht.

Beim Code-First-Ansatz haben Sie die Möglichkeit, leistungsfähige deklarative Vertragsprogrammierkonstrukte zu verwenden, die von verschiedenen Web Service-Stacks (WCF, ASMX, JAX WS usw.) bereitgestellt werden. Auf diese Weise können Sie mithilfe Ihrer bevorzugte Programmiersprache in Ihrem bevorzugten Editor modellieren. So können anstelle WSDL- und XML-Schemakonstrukte zu erlernen, Programmierungskonstrukte und Datentypen verwenden, mit denen Sie bereits vertraut sind. Der zugrunde liegenden WS-Stapel sorgt für die Arbeit zum Generieren von WS-Verträgen in ihrer systemeigenen Form (WSDL und XSD). Diese deklarativen Programmierkonstrukte machen es außerdem viel einfacher, einen völlig neuen Webdienst ins Leben zu rufen oder eine vorhandene Implementierung als Dienst offen zu legen.

Jedoch könnte diese Einfachheit und Bequemlichkeit zu schwierigen Problemen führen, wenn ohne das Bewusstsein der verwendeten zugrunde liegenden WSDL-/XSD-Konstrukte diese für die Darstellung der modellierten Artefakte eingesetzt werden. Ja, dies widerspricht den oben beschriebenen Anweisungen über die Möglichkeit, Ihre Verträge ohne Wissen von WSDL und XSD zu entwickeln. Aber die Wahrheit ist leider, dass die Code-First-Abstraktion Fehler aufweist. Wir versuchen, zu verstehen, warum aus .NET Entwickler-Sicht.

Wenn Sie mit dem Code arbeiten, haben Sie immer noch den Anspruch, Objektdiagramme mit dem .NET Typsystem zu modellieren. Beispielsweise wird der Code-First-Ansatz Sie nicht davon abhalten, die .NET-Eigenheiten z. B. System.Data.DataSet oder System.DateTimeOffset zu verwenden. Diese Typen können definitiv in XML dargestellt werden (und sie sind XML-serialisierbar). Jedoch hat ein Nicht-.NET-Client keinen Zugriff auf die gleichen Typen.

Ein weiteres gutes Beispiel sind zyklische Diagramme. Wie bereits zuvor erwähnt, haben Sie mit Code-First noch eine objektorientierte Denkweise. Folglich tendieren Sie dazu, Objektdiagramme zu modellieren, und diese Diagramme können möglicherweise zyklische Verweise haben. Berücksichtigen Sie die folgende Datenstruktur mit einem zyklischen Verweis:

public class OrderItem
    {
        public Order Order { get; set; }
    }

    public class Order
    {
        public List<OrderItem> Items { get; set; }
    }

Ein Order enthält eine Liste von OrderItems und jedes OrderItem hat einen Verweis zurück auf den übergeordneten Auftrag. Wenn Sie versuchen, dies in XML darzustellen, stoßen Sie auf ein Problem, nämlich dass XML über kein Konzept der Objektidentität verfügt, sodass Sie etwas Ähnliches wie XML in Abbildung 2 erhalten würden, was kontinuierlich fortgesetzt würde.

Abbildung 2 Darstellung eines Objektdiagramms mit zyklischen Verweisen in XML

<Order>
  <Items>
    <OrderItem>
      <Order>
        <Items>
          <OrderItem>
            ...
          </OrderItem>
        </Items>
      </Order>
    </OrderItem>
  </Items>
</Order>

Dieses Verhalten geht zurück auf die Differenz zwischen der hierarchischen Natur der XML-Dokumente und dem Diagrammmodell in Objektdiagrammen. Leider ist dies etwas, das nicht leicht zu erkennen ist, wenn Sie einem Code-First-Ansatz folgen. Um dieses Problem zu beheben, müssen Sie die Verweisnachverfolgung im DataContractSerializer über die IsReference-Eigenschaft in DataContractAttribute aktivieren. Aber dann werden Sie mit anderen Problemen konfrontiert: Zunächst könnten Sie das XML-Dokument nicht anhand eines Schemas mit einer Standardschemavalidierungs-API prüfen. Zweitens basiert der vom Serialisierungsprogramm verwendete Mechanismus für die Verweisprotokollierung auf den in Abschnitt 5 der in SOAP 1.1-Spezifikation definierten Codierungsregeln. Diese Regeln sind in Document-/Literal Style-Nachrichten veraltet, also den in WS-I-Spezifikationen angegebenen Standard.

Trotz dieser Probleme im Code-First-Ansatz finden Sie es möglicherweise noch einfacher, WCF Programmierkonstrukte anstelle von WSDL-/XSD-Konstrukten zu verwenden, da Sie ein wenig Kenntnisse darüber haben, wie diese beiden Welten einander zugeordnet werden und wie die WCF-Serialisierung funktioniert. Obwohl die Besprechung der Details des „Code-First Contract-First“-Ansatzes den Rahmen dieses Artikels sprengen würde, sollte Ihnen das folgende Diagramm eine 6.000-Meter-Ansicht der Beziehung zwischen der am häufigsten verwendeten WCF-Vertragsprogrammierkonstrukte und WSDL-/XSD-Konstrukte bieten.

Abbildung 3 Zuordnung zwischen häufig verwendeten deklarativen Programmierungskonstrukten in WCF- und WSDL-/Schemakonstrukten

Die wichtigste Alternative zum Code-First-Entwurf ist der Schema-First-Entwurf, die WSDL und XSD direkt für die Modellierung verwendet. Dies ist eine natürlichere Möglichkeit der WS-Vertragsmodellierung, da Sie jetzt mit systemeigenen Konstrukten zu tun haben. Die Verwendung von WSDL und XSD bietet Ihnen die Möglichkeit, Ihre Verträge mit einer XML-orientierten Denkweise zu modellieren, wodurch viele der Mängel des Code-First-Ansatzes beseitigt sind.

Wenn Sie diesen Weg gehen möchten, müssen Sie jedoch WSDL und XSD sehr gut kennen. Da diese beiden Welten miteinander verbunden sind, können Sie sich nicht vollständig auf WSDL und XSD konzentrieren. Z. B. beim Schema-First-Ansatz müssen Sie zu einem bestimmten Zeitpunkt aus Ihrem Schema Code generieren, um die Datenstrukturen programmgesteuert zu bearbeiten. Verschiedene heute verfügbaren WS-Stacks bieten Tools dafür (die verfügbaren Tools für WCF werden weiter unten in diesem Artikel behandelt). Aufgrund der Größe der XML-Schemaspezifikation unterstützen diese Tools jedoch häufig nur eine Teilmenge der Schemakonstrukte. Wenn Sie deshalb diese nicht unterstützten Konstrukte in Ihrem Schema verwenden, können Sie weiterhin auf Probleme hinsichtlich der Interoperabilität stoßen. Darüber hinaus haben bestimmte Dinge, die Sie modellieren, wie XSD-Einschränkungen, in zahlreichen Toolkits keine deklarative Entsprechung und können so während der Codegenerierung verschwinden.

Beim Code-First-Ansatz schreiben Sie Code und lassen das Toolkit WSDL/XSD für Sie generieren. Wenn Sie etwas in Ihrem Code ändern, wird dies automatisch in den generierten Schemas übernommen. Beim Schema-First-Ansatz können WSDL und XSD ohne einen entsprechenden Änderungsprozess und ohne disziplinierte Entwickler im Team veraltet werden. Darüber hinaus benötigen Sie möglicherweise auch die Tools für die Modellierung Ihrer WSDLs, da die IDE diese Features eventuell nicht hat.

Im Allgemeinen bieten Code-First und Schema-First Vor- und Nachteile. Von der Schema-First-Methode profitieren Sie insbesondere in Szenarien, in denen Sie vorhandene Schemas verwenden möchten, da Sie bei sehr frühen Phasen des Entwicklungszyklus modelliert wurden, um zu einer Vereinbarung mit den Beteiligten zu kommen. Dies ist eine häufig auftretende Situation in Regierungsbehörden und Banken. Möglicherweise müssen Sie auch die vorhandenen Schemas verwenden, wenn Sie Anwendungen erstellen, die vorhandenen Industriestandards entsprechen, wie z. B. Open Travel Alliance (OTA). Die Wahl zwischen zwei Ansätzen für die Vertragsdefinition sollte auch auf Ihrem Szenario, den Ressourcen und Fähigkeiten in Ihrem Team basieren. Beispielsweise, wenn Sie einen WCF-Dienst erstellen, der keine Clients unter anderen Plattformen unterstützen muss, möchten Sie wahrscheinlich keinen Schema-First-Ansatz in Betracht ziehen. Wenn Sie sehr vertraut sind mit Vertragsprogrammierungskonstrukten in WCF, den Serialisierungsvorgang gut kennen und sich auf die hierarchischen Strukturen fokussieren anstelle von Objektdiagrammen, können Sie den Code-First-Ansatz anwenden und die gleichen Ergebnisse erzielen.

Wenn Sie jetzt also beschlossen haben, den Schema-First-Ansatz anzuwenden, zeigt der restliche Artikel die Vorgehensweise für WCF-Dienste.

Schema-First-Ansatz erläutert

Der Schema-First-Ansatz hat fünf diskrete Schritte wie in Abbildung 4 dargestellt.


Abbildung 4 Schema-First Contract-First-Entwicklungsprozess

Wir haben bereits die ersten drei Schritte am Anfang dieses Artikels behandelt. Daher fassen wir sie einfach zusammen und fahren mit den nächsten Kernpunkten fort.

Schritt 1 Modellieren von Daten: Sie modellieren die Datenstrukturen zum Übertragen von Daten zwischen Ihrem Dienst und Clients mit dem XML-Schema. Sie können den Visual Studio-XSD-Editor oder ein ähnliches Tool, wie Altova XmlSpy oder Liquid XML Studio, verwenden.

Schritt 2 Modellieren von Nachrichten: Wie wir bereits erwähnt haben, modellieren Sie in diesem Schritt die Struktur Ihrer Meldungstexte mit dem XML-Schema. Sie werden wahrscheinlich die Datenstrukturen verwenden, die Sie in Schritt 1 modelliert haben.

Schritt 3 Modellieren der Schnittstelle: In diesem Schritt modellieren Sie Ihren Schnittstellenvertrag durch die Definition der Operationen. Allerdings müssen Sie hier im Gegensatz zu früheren Schritten das WSDL- anstelle des XML-Schemas verwenden. Bearbeiten von WSDL ist nicht verfügbar in Visual Studio, steht jedoch in bestimmte Produkten wie Altova XmlSpy zur Verfügung.

Schritt 4 Generieren von Code: Sobald Sie Ihre Daten, Nachrichten und Vorgänge modelliert haben, ist es Zeit zum Generieren von Code zur Darstellung dieser Elemente in Ihrer bevorzugten Programmiersprache. Die nächsten beiden Abschnitte erläutern, wie dies mit den heute verfügbaren Tools für WCF-Entwickler geschieht.

Schritt 5 Iterate-Vertragsentwurf und Codegenerierung: Es ist äußerst schwer perfekt in einem Versuch zu sein. Nachdem Sie Ihren Vertrag entworfen haben, sollten Sie ihn mehrere Male umgestalten, bis Sie die optimalen Strukturen finden.

Verwenden von standardmäßigen WCF-Tools für die Schema-First Contract-First-Entwicklung

Um dieses Szenario zu demonstrieren, erstellen wir einen einfachen Webdienst, der eine Operation zum Abrufen einer Liste der aktiven Prozesse auf dem Host-Computer bietet. Beginnend mit dem Datenvertrag modellieren wir zwei komplexe Typen.

  1. Komplexer Typ Prozess stellt einen Prozess auf dem Host-Computer dar.
  2. Komplexer Typ processList stellt eine Liste der von Prozesstyp definierten Prozesse dar.

Dann modellieren wir zwei Elemente explore und ExploreResponse, um den Textkörper der Input/Output-Nachrichten zu abstrahieren. Wie bereits erwähnt, um mit WS-I kompatibel zu sein, müssen Sie sicherstellen, dass der Nachrichtentext nur über ein untergeordnetes Element im Element soap:body verfügt. WCF und ASMX behandeln dies, indem Sie ein Wrapperelement mit dem Vorgangsnamen erstellen. Wenn die Benennungskonvention für die Modellierung von Nachrichten vom WCF-Standard abweicht, generieren die Codegenerierungstools MessageContract-Klassen für die Anforderung und Antwort. Wir verwendeten die WCF-Namenskonvention, um die Nachrichtenelemente zu benennen und somit ein einfacheres Objektmodell zu haben, wenn wir den Code später in diesem Prozess generieren.

Wir modellieren schließlich die WSDL mit einem externen Tool. In diesem Fall verwendeten wir Altova XML Spy, um uns zu helfen.

Die Abbildungen 4, 5 und 6 enthalten die oben genannten Daten-, Nachrichten- und Schnittstellen-Vertragsdefinitionen.

Abbildung 4 Data.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           xmlns:ns1="http://schemas.thinktecture.com/contractfirst/2009/07/data" 
           targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/data" 
           elementFormDefault="qualified" 
           attributeFormDefault="unqualified"
           >
    <xs:complexType name="process">
        <xs:sequence>
            <xs:element name="pid" type="xs:int"/>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="processList">
        <xs:sequence>
            <xs:element name="process" type="ns1:process" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Abbildung 5 Messages.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:data="http://schemas.thinktecture.com/contractfirst/2009/07/data"
           xmlns:ns1="http://schemas.thinktecture.com/contractfirst/2009/07/"
           targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/"
           elementFormDefault="qualified">
  <xs:import namespace="http://schemas.thinktecture.com/contractfirst/2009/07/data"
             schemaLocation="data.xsd"/>
  <xs:element name="explore">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="hostname" type="xs:string" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="exploreResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="processes" type="data:processList" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>  
</xs:schema>

Abbildung 6 ProcessExplorerService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
                  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
                  xmlns:tns="http://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
                  xmlns:msg="http://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:ns="http://schemas.thinktecture.com/contractfirst/2009/07/data"
                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
                  targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/">
    <wsdl:types>
        <xs:schema targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/" 
               elementFormDefault="qualified">
            <xs:import schemaLocation="messages.xsd"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="exploreRequestMessage">
        <wsdl:part name="parameters" element="msg:explore"/>    
    </wsdl:message>
    <wsdl:message name="exploreResponseMessage">
        <wsdl:part name="parameters" element="msg:exploreResponse"/>
    </wsdl:message>
    <wsdl:portType name="processExplorer">
        <wsdl:operation name="explore">      
            <wsdl:input wsaw:Action="http://schemas.thinktecture.com/contractfirst/2009/07/explore" 
                  message="tns:exploreRequestMessage"/>
            <wsdl:output wsaw:Action="http://schemas.thinktecture.com/contractfirst/2009/07/exploreResponse" 
                   message="tns:exploreResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="processExplorerHttpBinding" type="tns:processExplorer">
        <soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="explore">
            <soap12:operation 
        soapAction="http://schemas.thinktecture.com/contractfirst/2009/07/explore" 
        soapActionRequired="true"/>
            <wsdl:input>        
        <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="ProcessExplorerService">
        <wsdl:port name="processExplorerPort" binding="tns:processExplorerHttpBinding">
            <soap12:address location="http://localhost/processexplorer"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Sobald die Verträge fertig sind, können wir eine Visual Studio 2008-Eingabeaufforderung aufrufen und für WCF C#-Code generieren, indem wir svcutil.exe wie im folgenden Code dargestellt, verwenden:

svcutil -noconfig -serializer:datacontractserializer -d:../ -
namespace:*,Thinktecture.Samples.ProcessExplorerService
ProcessExplorerService.wsdl Messages.xsd Data.xsd

Svcutil.exe wird hauptsächlich verwendet, um Code für die Clientseite zu generieren. Im Gegensatz zur Vorgänger wsdl.exe verfügt es über keine Option zum Generieren des dienstseitigen Skeletts. Daher sehen Sie, dass der generierte Code clientseitigen Proxy-Code enthält, der nicht für die dienstseitige Implementierung erforderlich ist. Für die Dienstimplementierung müssen Sie diese nicht relevanten Typen aus dem generierten Code entfernen. In unserem Beispiel tun wir dies durch das Entfernen der ProcessExplorerClient- und ProcessExplorerChannel-Klassen aus dem generierten Code. Nachdem Sie eingerichtet haben, können Sie den generierten Schnittstellentyp processExplorer implementieren, wie im ProcessExplorerService1-Projekt in der zugehörigen Beispielprojektmappe dargestellt.

Schließlich müssen Sie vor dem Hosten des Diensts ServiceMetadataBehavior anpassen, um die automatische Generierung von WSDL zu deaktivieren und die WCF-Metadaten-Generierungslaufzeit auf die statische WSDL-Datei zu verweisen. Im folgenden wird ein externer Metadatenspeicherort in der WCF-Konfiguration angegeben:

<serviceMetadata httpGetEnabled="true"                            
externalMetadataLocation="..\contracts\processexplorerservice.wsdl"/>

Wie Sie möglicherweise bemerkt haben, könnte die Schema-First-Entwicklung mit dieser Vorgehensweise ein wenig überwältigend in realen Anwendungen sein. WSCF.Blue, der Nachfolger des kostenlosen WSCF-Tools (Web Service Contract First) wurde erstellt, um die Lücken zu schließen.

Ein Blick auf WSCF.blue

WSCF.blue ist ein kostenloses Visual Studio 2008-Add-In. Seine Hauptfunktion ist die Aktivierung der Schema-First Contract-First-Entwicklung in Ihrer bevorzugten IDE. Sie modellieren Ihre Daten- und Nachrichtenverträge entweder im Visual Studio XML-Schema-Editor oder in einem kostenlosen Tool wie Liquid XML-Editor. Sie können letzteren bevorzugen, da der in Visual Studio 2008 verfügbare Schema-Editor weniger Modellierungsunterstützung als der aus Visual Studio 2005 bietet. Sobald die Schemas bereit sind, können Sie den WSDL-Generierungsassistenten von WSCF.blue für die Dienstschnittstellenmodellierung verwenden. Es stellt eine gute Möglichkeit dar, die Vorgänge Ihrer Schnittstelle zu modellieren und blendet die Details der tatsächlichen WSDL-Konstruktion aus. Dieser Assistent kann durch Klicken mit der rechten Maustaste auf die XSD-Datei gestartet werden, die Ihre Nachrichtenverträge enthält, und durch Auswählen des Menüelements Create WSDL Interface Description aus dem Kontextmenü, wie in Abbildung 7 dargestellt.


Abbildung 7 Starten des WSDL-Generierungsassistenten

Dieser Assistent besteht aus wenigen Schritten zum Sammeln von Informationen über die Vorgangsverträge in der Dienstschnittstelle. Die meisten Schritte sind ziemlich einfach. Es gibt jedoch einige Dinge, die Ihre Aufmerksamkeit in den Schritten 2, 3 und 4 erfordern.

Die definierten Dateitypen, die Sie in der Schemadatei im Projektmappen-Explorer ausgewählt haben, um den Assistenten zu starten, stehen für die WSDL-Modellerstellung zur Verfügung. Wenn Sie zusätzliche Typen in anderen XSD-Dateien haben, zeigt Ihnen Schritt 2, wie sie diese auch importieren können, wie in Abbildung 8 dargestellt.


Abbildung 8 Importieren zusätzlicher Schemadokumente in den Assistenten

In Schritt 3 modellieren Sie Ihre Vorgänge. Der Assistent verfügt auch über ein Feature, das Ihre Nachrichtenvertragselemente durchlaufen und Vorgänge ableiten kann. Dieses Feature funktioniert jedoch nur für Nachrichten mit den Namenskonventionen der Ableitungsregeln des Assistenten, wie in Abbildung 9 dargestellt.


Abbildung 9 Hinzufügen oder Entfernen von Vorgängen in der Schnittstelle

In Schritt 4 können Sie die Nachrichten zuordnen, die in Anforderungen und Antworten-Operationen modelliert wurden. Wenn das Ableitungsfeature in Schritt 3 verwendet wurde, wird diese Zuordnung automatisch vom Assistenten erfolgen. Sie können auch Nachrichtenkopfzeilen in Ihren Anforderungs- und Antwortnachrichten während dieses Schritts definieren (Abbildung 10).


Abbildung 10 Definieren von E/A-Nachrichten in Vorgängen

Schließlich wird beim Ausführen des Assistenten die generierte WSDL-Datei zum Projekt hinzugefügt.
Nun, da der Vertrag modelliert ist, können Sie das WSCF.blue-Codegenerierungstool verwenden, um WCF-Code in Ihrer Projektsprache zu generieren. Klicken Sie mit der rechten Maustaste auf die WSDL-Datei im Projektmappen-Explorer und wählen Sie das Web Service-Code-Menüelement im Kontextmenü, um das Codegenerierungstool (Abbildung 11) zu starten.


Abbildung 11 Starten des Codegenerierungstools

Option Beschreibung
Clientseitiger Proxy Generiert den clientseitigen Proxy für Dienstconsumer.
Dienstseitiges Stub Generiert den dienstseitigen Code für Dienstimplementierer.
Öffentliche Eigenschaften Legt generierte Klassenmember als öffentliche Eigenschaften offen.
Serialisierbare Klassen Generierte Klassen werden mit SerializableAttribute ergänzt.
Auflistungen Verwendet den Typ Collection<T> für die Darstellung von Auflistungstypen.
List<T> Verwendet den Typ List<T> für die Darstellung von Auflistungstypen.
Datenbindung Generiert den Code, der zum Binden von generierten Klassen an bindbare Datenkomponenten erforderlich ist.
Reihenfolgebezeichner Generiert eine Order-Eigenschaft für jedes verwendete DataMemberAttribute.
Async-Methoden Generiert die asynchrone Version der Operation und legt die OperationContextAttribute.AsyncPattern-Eigenschaft auf True fest, um WCF mitzuteilen, dass sie vorhanden sind.
Separate Dateien Jede generierte Klasse wird in eine eigene Datei platziert.
Anpassen von Groß- und Kleinschreibung Verwendet die Pascal-Schreibweise bei der Benennung der generierten Klassen und ihrer Eigenschaften ohne Beeinträchtigung des Serialisierungsvorgangs.
Konfliktmodus Gibt den Wert für die ServiceBehaviorAttribute.ConcurrencyMode-Eigenschaft an.
Instanzenkontextmodus Gibt den Wert für die ServiceBehaviorAttribute.InstanceContextMode-Eigenschaft an.
Verwenden Sie Synchronisierungskontext Gibt den Wert für die ServiceBehaviorAttribute.UseSyncrhronizationContext-Eigenschaft an.
WSDL-Endpunkt aktivieren Fügt einen Endpunkt zum Dienst hinzu, um die statischen Metadatendateien offen zu legen.Dies ist wichtig, wenn Sie statische Metadatendokumente in sich selbst gehosteten Diensten verfügbar machen möchten.
Name der Zieldatei Namen der Ausgabedatei.Wenn die Option Separate Dateien aktiviert ist, wird dieser Parameter verworfen, und es werden Klassennamen als Dateinamen verwendet.
Ziel-Namespace CLR-Namespace für die generierten Typen.
Einstellungen speichern Speichert die zuletzt verwendeten Codegenerierungsoptionen.
Vorhandene Dateien überschreiben Gibt an, dass WSCF.blue vorhandene Dateien überschreibt anstatt eindeutige Dateinamen zum Auflösen von Konflikten zu erstellen.Es ist wichtig, zu beachten, dass alle Änderungen am generierten Code überschrieben werden.Daher empfehlen wir, solche Änderungen über partielle Klassen vorzunehmen.

Das Dialogfeld zum Generieren von Code bietet verschiedene Codegenerierungsoptionen. Es enthält die meisten in svcutil.exe verfügbaren Optionen sowie einige zusätzliche, z. B.: Serverseitiges Stub zum Generieren von serverseitigen Code; Anpassen von Groß- und Kleinschreibung für die Konvertierung von Kamelschreibweisen im Schema in Pascalschreibweisen ohne Beeinträchtigung der Serialisierung; Trennen von Dateien zum Generieren einer Codedatei für jeden CLR-Typ, der für die Darstellung der entsprechenden Schema-Artefakte (Abbildung 12) generiert wurde. Abbildung 13 enthält eine vollständige Liste der Optionen in Codegenerierungstool.


Abbildung 12 Codegenerierungsoptionen

Abbildung 13 WSCF.blue-Codegenerierungsoptionen

Die in diesem Artikel (Beta 1 ab 25. Juli 2009) beschriebene WSCF.blue-Version unterstützt noch kein Zusammenführen von Konfigurationsdateien. Folglich wird die erforderliche Konfiguration zum Hosten Ihres Dienstes in eine output.config-Datei ausgegeben. Für die Verschiebung dieser Datei in Ihre web.config- oder app.config-Datei ist ein manueller Schritt erforderlich.

Wenn die Konfigurationsdatei bereit ist, können Sie mit der erforderlichen Implementierung in die generierte Dienstklasse fortfahren.

Schließlich können Sie die Schemas in Visual Studio ändern und WSDL-Roundtrips-Features im WSDL-Generierungsassistenten zum Ändern Ihrer Verträge und erneuten Generieren des Codes verwenden.

Ausblick

WSCF.Blue ist als Open Source-Projekt freigegeben und bei wscfblue.codeplex.com erhältlich. Die aktuelle Betaversion hat einige Einschränkungen, die Sie auf die grundlegenden, aber am häufigsten verwendeten Web Service-Implementierungen einschränken können. Der WSDL-Generierungsassistent unterstützt z. B. derzeit nur grundlegende HTTP-Bindung. Außerdem generiert die aktuellen Codegenerierung in WSCF.blue nur XmlSerializable-Klassen anstelle von DataContractSerializable-Klassen, um eine breitere Palette von Schemakonstrukten zu unterstützen. Eine vollständige Liste bekannter Probleme und eine Übersicht des Tools sind auf der CodePlex-Website verfügbar.

Eine ausführliche Anleitung zum Verwenden von WSCF ist verfügbar unter thinktecture.com/resourcearchive/tools-and-software/wscf/wscf-walkthrough. Das Dokument basiert zwar auf WSCF 0.6, die erläuterten Schritte gelten aber gleichermaßen für

Schlussbemerkung

Die Modellierung von Webdiensten gemäß „Schema-First, Contract-First“ bietet Ihnen die Möglichkeit, Ihre Verträge XML-zentriert zu modellieren. So bleibt Ihr Fokus auf universell akzeptierten Typen und den in XML dargestellten hierarchischen Datenstrukturen. Toolsunterstützung ist wichtig, damit eine Plattform für Webdienste erfolgreich ist. WSCF.blue erweitert den von WCF bereitgestellten stadardmäßigen Toolseinsatz, um schemabasierte Contract-First-Entwicklung für WCF-Entwickler zu erleichtern.

Christian Weyer ist Mitbegründer und leitender Architekt von Thinktecture und hat verteilte Anwendungen mit Java, COM, DCOM, COM+, Webdiensten, WCF und anderen Technologien seit vielen Jahren modelliert und implementiert. Christian ist der ursprüngliche Erfinder der klassischen Web Services Contract First-Tools für .NET. Nehmen Sie Kontakt mit ihm unter thinktecture.com/staff/christian auf.

Buddhike de Silva Ist Ingenieur in Australien. Bevor er nach Australien zog, war er der leitende Ingenieur bei Thinktecture. Buddhike entwirft und implementiert verteilte Anwendungen seit vielen Jahre und er ist außerdem ein seit langer Zeit Mitwirkender bei WSCF/WSCF.blue-Projekten. Nehmen Sie Kontakt mit ihm unter http://blogs.thinktecture.com/buddhike auf.

1 Entwerfen nach Vertrag, Technical Report TR-EI-12/KT, Interactive Software Engineering Inc., 1986.

2 http://se.ethz.ch/~meyer/publications/computer/contract.pdf

3 RESTful-Dienste kommunizieren jedoch mit Ihren Clients durch den standardmäßigen Austausch von HTTP-Nachrichten und folgen einem völlig unterschiedlichen Paradigma.