.NET 3.0: Einheitliche Kommunikation über die WCF

23.01.2007 von Ulrich Pröller
Mit dem .NET Framework 3.0 liefert Microsoft die Windows Communication Foundation (WCF) aus. Damit sollen die verschiedenen Kommunikationsansätze der Windows-Welt endlich unter dem Dach eines neu entwickelten API zusammengeführt werden.

Wer sich bislang vor die Aufgabe gestellt sah, seine Windows-Applikation mit anderen Prozessen kommunizieren zu lassen, hatte die Wahl zwischen einer Vielzahl verschiedener Möglichkeiten – Enterprise Services, System.Messaging, WSE oder .NET Remoting. Je nachdem, ob die Kommunikation zwischen Applikationen auf der gleichen Maschine, im LAN oder über das Internet stattfand, ob synchron oder asynchron, uni- oder bidirektional, verschlüsselt, als Binär- oder XML-Datenstrom, ob auf beiden Seiten die gleiche oder verschiedene Plattformen zum Einsatz kamen: Für jede Anforderung konnte und musste sich der Entwickler für die jeweils richtige Plattform entscheiden.

Deren jeweilige Architekturen sind so verschieden wie die Zeiten, in denen die jeweiligen Technologien entstanden. Die Entwicklung unter COM+ erfordert vom Entwickler eine andere Herangehensweise als die Implementierung eines Webservice. In der Praxis findet man jedoch nur wenige Entwickler, die alle Kommunikationstechnologien gleichermaßen beherrschen. Und so wurde und wird oft nicht die Technik genutzt, die der Aufgabenstellung angemessen ist, sondern jene, die der Entwickler beherrscht.

Vereinheitlichte Kommunikation durch die WCF

Mit der Fertigstellung der Windows Communication Foundation (WCF, vormals Indigo) als Teil des .NET Framework 3.0 wird dies anders. Die WCF fasst alle bestehenden Kommunikationsansätze von Microsoft unter dem Dach eines einheitlichen APIs zusammen. Das Projekt startete unter dem Namen Indigo bereits im Jahr 2002. Einem Mitglied des Entwicklungsteams zufolge wurde der Code seitdem zweimal völlig neu geschrieben, bevor er Anfang November als Teil des .NET Framework 3.0 freigegeben wurde.

Alles in einer API: Die WCF ist die universelle Kommunikationsschnittstelle unter .NET 3.0. (Quelle: Microsoft)

WCF ist mehr als ein gemeinsames API über bestehenden Kommunikations-Stacks. Es kombiniert die Vorteile der verschiedenen Technologien:

• ASP.NET Web Services (ASMX): Interoperabilität mit Webservices auf verschiedenen Plattformen

• Web Services Enhancements (WSE): Kompatibilität mit WS.*-Protokollen

• System.Messaging: Unterstützung für asynchronen, zuverlässigen Nachrichtenaustausch mit Hilfe der Windows Message Queues

• Enterprise Services: Bietet als managed API für COM+ Unterstützung für unternehmenskritische Transaktionen

• .NET Remoting: Ermöglicht eine einfache und transparente Kommunikation zwischen .NET-Applikationen

ABC als Mantra

WCF erleichtert jedoch dem Entwickler nicht nur die Arbeit, es zwingt ihn auch dazu, expliziter als bisher zu beschreiben, was er will. Dies tut er über das Mantra der Windows Communication Foundation, das „ABC“. Es steht für Address, Binding und Contract und beschreibt vollständig das Verhalten einer WCF-Applikation gegenüber der Außenwelt.

ABC: Address, Binding und Contract gehören zu jeder Kommunikation über die WCF dazu. (Quelle: Microsoft)

Address steht für die Adresse, unter der die Applikation erreichbar ist. Die Adresse hat immer die Form eines Uniform Resource Identifiers (URI). Im Schemateil der URI wird das verwendete Transportprotokoll beschrieben, zum Beispiel „HTTP“ oder „TCP“. Im hierarchischen Teil findet die eindeutige Adresse Platz, deren Format von der verwendeten Transporttechnik abhängt. So bezeichnet die Adresse HTTPS://prosa1:8005/ServiceModelSamples/CalculatorService einen Endpunkt auf dem Rechner PROSA1, der das HTTPS-Protokoll auf Port 8005 nutzt.

Binding beschreibt, wie die Applikation mit der Außenwelt kommuniziert. Man kann sich ein Binding als einen Stack aus einzelnen Binding-Elementen vorstellen. Jedes Binding-Element beschreibt einen Aspekt der verwendeten Kommunikationstechnik wie zum Beispiel die Transportschicht (TCP oder HTTP), die Kodierung (Binär oder XML) oder die zum Einsatz kommende Verschlüsselungstechnik.

Der Contract bezeichnet die Interfaces und Datenstrukturen, die eine Applikation anbietet. Die WCF kennt drei Haupttypen von Contracts: Service Contracts, Data Contracts und Message Contracts.

Service Contracts

Der Service Contract beschreibt eine Reihe zusammenhängender Operationen in einem gemeinsamen Interface. Das folgende Interface ICalculator

public interface ICalculator
{
double Add(double n1, double n2);
double Subtract(double n1, double n2);
double Multiply(double n1, double n2);
double Divide(double n1, double n2);
}

bietet vier Methoden zum Ausführen einfacher Berechnungen an. Um daraus einen Service Contract zu machen, genügt es, das Interface mit einem [ServiceContract] Attribut auszustatten.

// Define a calculator service contract.
[ServiceContract(Namespace="http://tecCHANNEL.Samples")]
public interface ICalculator
{
double Add(double n1, double n2);
double Subtract(double n1, double n2);
double Multiply(double n1, double n2);
double Divide(double n1, double n2);
}

Das Attribut akzeptiert als optionales Argument einen Namespace, dessen Aufgabe es ist, den Interface-Namen eindeutig zu machen.

Das oben stehende Beispiel definiert jetzt zwar einen Service Contract „tecCHAN-NEL.Samples.ICalculator“, doch dieser Service würde über keine Methoden verfügen. Denn obwohl das Interface ICalculator die vier Methoden Add, Substract, Multiply und Divide enthält, werden diese nicht automatisch Teil des Service Contracts. Um dies zu erreichen, müssen die Methoden ebenfalls mit einem Attribut versehen werden:

// Define a calculator service contract.
[ServiceContract(Namespace="http://tecCHANNEL.Samples")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}

Erst jetzt werden die vier Interface-Methoden auch als Methoden des Service Contracts nach außen exponiert. Hier wird ein zentrales Designprinzip der Windows Communication Foundation deutlich: Alle Methoden oder Datenelemente, die nach außen sichtbar werden sollen, muss der Entwickler über Attribute als solche kennzeichnen. Dies bedeutet einen klaren und bewussten Bruch mit den Gepflogenheiten unter .NET Remoting, wo es möglich ist, ein komplexes Interface einfach dadurch von außen zugänglich zu machen, indem es von der Klasse MarshalByRefObject abgeleitet wird.

Data Contracts

Wenn wir versuchen, das ICalculator Interface um eine Methode zur Division ganzer Zahlen zu erweitern, DivisionResult IntegerDivide(int i1, int i2); müssen wir eine Datenstruktur DivisionResult einführen.

public class DivisionResult
{
public int IntegerResult; // Ganzzahliges Ergebnis
public int Remainder; // Ganzzahliger Rest
}

Doch während WCF einfache Datentypen wie int, double oder string direkt serialisiert, gilt für komplexe (zusammengesetzte) Datentypen das Gleiche wie für Methodenaufrufe: Nur was explizit als solches gekennzeichnet ist, geht auch über die Leitung. Um unsere Datenklasse DivisionResult serialisieren zu können, muss daraus ein Data Contract werden.

[DataContract]
public class DivisionResult
{
[DataMember]
public int IntegerResult;
[DataMember]
public int Remainder;
}

Auch hier wird wieder deutlich, dass es nicht genügt, [DataContract] über eine Datenstruktur zu schreiben, sondern dass es notwendig ist, alle zu übermittelnden Datenelemente einzeln mit Attributen zu versehen. Möchte man in einer Datenstruktur nicht alle Elemente serialisieren, so genügt es, bei den auszuschließenden Members das [DataMember] Attribut wegzulassen.

Die Attribute [DataContract] und [DataMember] werden in diesem Beispiel in ihrer einfachsten Form ohne Argumente verwendet. Sie bringen eine Reihe optionaler Parameter mit, die es unter anderem ermöglichen, Datenstrukturen effizient versionieren zu können.

Message Contracts

Die Windows Communication Foundation basiert im Kern auf der asynchronen Übertragung von Nachrichten. Auch wenn wir die Methode eines Service Contract synchron aufrufen, wird dies „unter der Haube“ in zwei asynchrone Nachrichten – Aufruf der Methode und Rückgabe des Ergebniswerts – übersetzt. Das WCF Framework sorgt dafür, dass der Benutzer davon nichts merkt – solange er das will.

Universell: Die WCF ist nicht auf die Windows-Welt beschränkt. (Quelle: Microsoft)

Es gibt jedoch Situationen und Aufgabenstellungen, wo es wichtig ist, Format und Inhalt der ausgetauschten Nachrichten selbst verändern zu können. Dies ist zum Beispiel dann der Fall, wenn es um Applikationen zum Routen von WCF-Nachrichten oder zur gleichmäßigen Lastverteilung geht. Für diesen Zweck gibt es die Message Contracts, die es erlauben, die ausgetauschten Nachrichten selbst zu definieren.

WCF und .NET Remoting

Es ist weiter oben schon angeklungen: Die Paradigmen von .NET Remoting und der Windows Communication Foundation unterscheiden sich in einigen wesentlichen Punkten. Während .NET Remoting versucht, die Interprozesskommunikation für den Entwickler so einfach und transparent wie möglich zu machen, verfolgt die WCF eine andere Strategie. Sie verlangt vom Entwickler, über detaillierte Kontrakte explizit anzugeben, was er wie sichtbar und serialisierbar machen möchte.

Dieser Paradigmenwechsel hat einen Grund. Unter .NET Remoting ist es sehr leicht, Objekte vom Server auf einen Client zu remoten und dort so zu verwenden wie eine lokale Objektinstanz. Der Unterschied zwischen lokalen und Remote-Objekten ist für den Entwickler nicht mehr zwangsläufig sichtbar. Als Folge sieht man immer wieder extrem ineffiziente Softwaredesigns, wo zum Beispiel tausende von Remoting Calls pro Sekunde abgesetzt werden, nur um Properties aus Remote-Objekten abzufragen.

Das Problem von .NET Remoting ist also nicht, dass es nicht oder nicht effizient genug funktioniert, sondern dass es den Entwicklern eine Transparenz und Leichtigkeit im Aufruf von Remote-Objekten vorgaukelt, die es so nicht gibt. .NET Remoting möchte die Schwierigkeiten bei der Kommunikation über Applikations-, Rechner- und Netzwerkgrenzen vor dem Entwickler verbergen.

Wie die Erfahrung gezeigt hat, ist es aber wichtig, den Entwickler bei der Verwendung eines Kommunikations-Frameworks zu zwingen, sich Gedanken über die Architektur seiner verteilten Applikation zu machen. Dies ist es, was die WCF tut: Sie fordert Service- sowie Data Contracts und darüber hinaus noch Angaben über das zu verwendende Binding und die zu benutzende Adresse.

Ausblick

Bestehende Applikationen, die .NET Remoting verwenden, laufen natürlich auch unter .NET 3.0 und werden weiterhin unterstützt. Gerade in begrenzten, lokalen Kommunikationsszenarien kann Remoting weiterhin das Mittel der Wahl sein. Manche Dinge wie die Übergabe von Objektgraphen sind via Remoting einfach zu realisieren:

public interface IFooFactory
{
IFoo GetFoo();
}
public class FooFactory : MarshalByRefObject, IFooFactory
{
public IFoo GetFoo() { return new Foo(); }
}
public interface IFoo
{
Void PerformServerAction();
}
public class Foo: MarshalByRefObject, IFoo
{
public void PerformServerAction()
{
Console.WriteLine(“Server action performed”);
}
}
class Program
{
static void Main(string[] args)
{

FooFactory factory = new FooFactory();
RemotingServices.Marshal(factory, factoryUri);

}
}

Konstruktionen dieser Art, wo über den Aufruf eines Remote Interface eine auf dem Server erzeugte Objektinstanz zum Client übertragen und von dort wie ein lokales Objekt verwendet werden kann, sind zwar auch unter WCF möglich, aber deutlich schwerer zu realisieren.

So ist es keineswegs immer notwendig oder sinnvoll, eine auf .NET Remoting beruhende Anwendung zu WCF zu migrieren. Wer aber an die Grenzen von .NET Remoting stößt und vor der Aufgabe steht, aus seiner Remoting-Applikation eine WCF-Applikation zu machen, dem seien die Artikel von Ingo Rammer im MSDN empfohlen, die die wesentlichen Schritte der Migration beschreiben. (ala)