Von der Datenbank bis zur Oberfläche mit .NET, Teil 2: Application Server und Webservices

Know-how  –  1 Kommentare

Nachdem Datenbank, Datenzugriffs- und Geschäftslogikschicht sowie ein Testclient auf Konsolenbasis entstanden sind, werden im zweiten Artikel des Tutorials die zwei physikalischen Schichten um den Application Server erweitert, der SOAP-Webservices für den Zugriff auf die Geschäftslogik bereitstellen soll.

Beim erstellten Testclient arbeiten Benutzeroberfläche, Geschäftslogik und Datenzugriff in einem Prozess. Nun ist mit der Windows Communication Foundation (WCF) ein SOAP-Webservice zur Prozesstrennung zu erstellen, wodurch sich Client und Backend auf mehrere Rechner aufteilen lassen. Wie Abbildung 1 zeigt, sollen Client und Server die Bibliothek WWWings_GO verwenden, die dann jeweils einmal auf Client und Server existiert.

Die Basis für alle folgenden Aktionen ist die Projektmappe "WWWings". Es werden im zweiten Teil des Tutorials drei weitere Projekte in dieser Projektmappe angelegt (siehe Abb. 1):

  1. Dienstimplementierung (WWWings_Dienste)
  2. Server für Dienste (WWWings_Server)
  3. Dienst-Proxies für Client (WWWings_Dienstproxies)
Zielarchitektur für zweiten Teil des Tutorials (Abb. 1)

Vor dem Anlegen der Projekte sollte der Entwickler aber erst eine Definition der Dienstschnittstellen schaffen. Diese legt er am besten in das Projekt WWWings_GO, weil das die Bibliothek für alles werden soll, was sich Client und Server an Code teilen können. GO steht dann nicht mehr nur für "Geschäftsobjekte", sondern für "Gemeinsame Objekte". Zum teilbaren Code gehören auch die Dienstschnittstellen. Zwar ließen sich die Definition wieder in ein eigenes Projekt auslagern, das Tutorial soll aber die Projektanzahl überschaubar halten.

Von der Datenbank bis zur Oberfläche mit .NET

Um das Beispiel mitzuprogrammieren, benötigt der Entwickler Grundkenntnisse in der C#-Syntax und der Handhabung von Visual Studio 2010. Das Tutorial verwendet die englische Version von Visual Studio, weil es in der deutschen Ausgabe einen Fehler gibt, durch den sich das Beispiel nicht ohne viel manuelle Arbeit zum Laufen bringen lässt.

Als Erstes erhält die WWWings_GO-Bibliothek eine zusätzliche Referenz auf die .NET-Bibliothek System.ServiceModel, die das Herzstück von WCF darstellt. Dann werden in WWWings_GO eine neue C#-Datei Dienstschnittstellen.cs mit einer Schnittstelle IBuchungsService samt sieben Dienstoperationen mit dem Inhalt aus Listing 1 und eine Nachrichtenklassen.cs mit dem Inhalt aus Listing 2 angelegt, die 14 Dienstnachrichtentypen für die sieben Operationen definiert (jeweils eine Anfrage- und eine Antwort-Nachricht).

Die Dienstschnittstelle IBuchungsService ist mit [ServiceContract] annotiert. Das deklariert, dass sie eine Webservice-Schnittstelle sein soll. In der Annotation wird ein Namensraum vergeben (typischerweise in Form einer URL, die nicht mit einer realen Web-Adresse verwechselt werden darf!).

Für die Definition der sieben Dienstoperationen kommt mit dem Nachrichtenmuster ein typisches Architektur-Pattern der serviceorientierten Welt zum Einsatz. Jede Operation hat genau einen komplexen Typ als Parameter (Anfragenachricht) und einen als Rückgabewert (Antwortnachricht). Selbst in Fällen, bei denen die Geschäftslogik komplexe Datenobjekte zurückgibt (z. B. GetFlug()), ist das Rückgabeobjekt nochmals in eine Nachricht verpackt – sogar in dem Fall, dass es gar keinen Parameter gibt (GetFlughaefen()).

Grundsätzlich lässt sich in WCF ohne diesen Nachrichtenstil arbeiten, was bedeutet, dass man primitive Datentypen wie string, int und bool sowohl als Parameter (mehrere sind möglich) als auch als Rückgabetyp (nur einer ist möglich) verwenden könnte. Die Operation GetFlug() würde dann so definiert werden:

  [OperationContract]
Flug GetFlug(int FlugNummer);

Allerdings sind solche Webservice-Operationen nicht gut versionierbar. Wenn bei einer zukünftigen Anforderung weitere Werte im Rahmen einer Operation auszutauschen sind, etwa ein weiterer Parameter "Fluggesellschaft", müsste man eine neue Operation

Flug GetFlug2(int FlugNummer, string Fluggesellschaft);

erzeugen. Methodenüberladung und optionale Parameter gibt es bei SOAP-Webservices nicht. Wenn man einfach die Definition von GetFlug() ändern würde, könnten die Operation in der Folge keine Clients mehr nutzen. Der Entwickler wäre gezwungen, Client und Server gleichzeitig umzustellen. Das ist aber nicht immer möglich, gerade dann nicht, wenn er Webservices für Clients bereitstellt, die (viele) andere nutzen.

Beim in Listing 1 und 2 gezeigten Nachrichtenstil ist die Versionierung kein Problem. Es lässt sich jederzeit die Anfrage- und/oder Antwort-Klasse um weitere Attribute erweitern. Bestehende Clients würden dann ein neues Attribut "Fluggesellschaft" nicht senden und WCF das Attribut mit seinem Standardwert (hier eine Leerzeichenkette) initialisieren. Die Webservice-Operation müsste darauf nur intern darauf entsprechend reagieren.

Listing 2 zeigt, dass die Nachrichtenobjekte jeweils mit [MessageContract] und [MessageBodyMember] annotiert sind. Das sorgt dafür, dass der Proxy-Generator im Client wieder einen Proxy im Parameterstil erzeugt, was der Versionierung nicht schadet, sondern dem Client-Entwickler nur etwas Tipparbeit erspart. Solche expliziten "Message Contracts" lassen sich auch dazu nutzen, Inhalte in den Header einer SOAP-Nachricht zu verlegen und einzelne Teile einer Nachricht zu signieren oder zu verschlüsseln.