WCF - Tutorial 1

Immer mehr Anwendungen sind vernetzt. Vor einigen Jahren waren Computerspiele mit Netzwerkunterstützung noch eine Seltenheit und heute kann man die meisten nicht einmal mehr Registrieren wenn man kein Internet besitzt. Aber auch Cloudcomputing und andere Netzwerkanwendungen rücken immer stärker in den Vordergrund.
Natürlich möchte man in einer modernen Zeit auch eine moderne Lösungen für die steigende Anzahl der Netzwerkprogramme. Keiner möchte sich mehr mit Socketprogrammierung rumschlagen.
Microsoft hat mit Windows Communication Foundation (WCF), einem Bestandteil aus .Net, einen Weg in die Zukunft eingeschlagen. Netzwerkanwendungen zu programmieren ist dabei auch dem Leihen ohne große Schwierigkeiten möglich.

Es gibt zwar schon unzählige WCF-Tutorials im Internet die ziemlich gut sind, doch da ich selber noch lerne dachte ich mir, dass es das beste ist selber darüber zu schreiben. So lernt man glaube ich am besten. In meinem ersten Tutorial zeige ich nur grob wie WCF funktioniert und lasse einen Client mit einem Host kommunizieren. In meinem zweiten Tutorial gehe ich tiefer in die Möglichkeiten von WCF ein und erstelle einen kleinen Chatserver an dem sich mehrere Teilnehmer einloggen können.
Zu beginn des Tutorials solltet ihr euch eine Projektmappe mit 2 Projekten erstellen. Beide Projekte sollten dabei einfache C#-Consolenanwendungen sein. Als Verweise fügt ihr beiden Projekten System.ServiceModel hinzu. Ich habe meine Projekte WCF_Tut_Client und WCF_Tut_Host genannt.
Als erstes erstellen wir eine Klasse mit Funktionen, die der Host dem Client anbieten soll. Auf diese Funktionen kann der Client später wie auf lokale Funktionen zugreifen. Eigentlich sollte man das alles als Interface aufsetzten, aber da ich es hier erst einmal einfach halten möchte schreibe ich das in eine normale Klasse. Fügt also dem Host eine Datei hinzu die HostFunctions heißt.

//HostFunctions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace WCF_Tut_Host
{
  [ServiceContract()]
  class HostFunctions
  {
    [OperationContract]
    public string getDate()
    {
      return DateTime.Today.DayOfWeek.ToString();
    }

    [OperationContract]
    public string getTime()
    {
      return DateTime.Now.ToString();
    }
  }
}

Wichtig sind dabei die Attribute über der Klasse und den Funktionen. Diese definieren einen Dienstvertrag mit einem WCF-Service.
Als nächstes müssen wir einen Service erstellen und diesen starten. Das ganze fügen wir der Main-Methode in der Program.cs vom Host hinzu.
//Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace WCF_Tut_Host
{
  class Program
  {
    static void Main(string[] args)
    {
      using (ServiceHost sh = new ServiceHost(typeof(HostFunctions)))
      {
        sh.Open();
        Console.WriteLine("Service started..." );
        Console.ReadLine();
        sh.Close();
      }
    }
  }
}

Wir erstellen einen ServiceHost sh. Damit der ServiceHost(SH) auch weiß was für Funktionen er hosten soll müssen wir ihm noch den Typen mit angeben. Das geschieht durch typeof( unsere Klasse ).
Mit sh.Open() starten wir unseren SH. Anschließend darf sich das Programm nicht gleich wieder beenden. Wir warten deshalb auf eine Benutzereingabe mit Console.Readline(). Nachdem der Benutzer Enter gedrückt hat beenden wir den SH mit sh.Close() und beenden unser Programm.
Jetzt haben wir einen SH und die passenden Funktionen dafür. Damit haben wir den Code für den Host fertig. Allerdings weiß unser Host noch nicht worauf er achten soll. Wo können sich beispielsweise Clients anmelden um die Funktionen nutzen zu können? Diese Informationen entnimmt der SH aus der App.config. Die App.config kann erstellt werden indem ein neues Element unserem Projekt hinzugefügt wird. Das ganze nennt sich Anwendungsconfigurationsdatei. Standardmäßig sollte diese Datei bereist App.config genannt werden.
Das ganze sieht nicht sehr spektakulär aus. Das ändern wir jetzt allerdings. In die Datei werden nun die Enpunkte eingetragen die der Service nutzen wird. Das ganze müssen wir zum Glück nicht per Hand machen. Microsoft liefert hierfür den Dienstconfigurationseditor. Dieser kann mit Visual Studio über Extras -> WCF-Dienstconf... oder im Startmenü unter Start -> Windows SDK -> Tools -> Dienstconfigurationseditor gestartet werden. In dem Editor öffnen wir nun unsere App.config aus unserem Projekt. Anschließend erstellen wir unter Services einen neuen Service. Der Service muss nun, wie der ServiceHost auch, wissen was wir überhaupt für Funktionen bereitstellen wollen. Wir wählen dafür unsere HostFunctions-Klasse aus. Das ganze geschieht durch die eingabe unseres Projektnamens und der Klasse. Wer schon eine .exe erstellt hat kann diese auch öffnen und erhält damit gleich alle verfügbaren Klassen, die einen Dienstvertrag anbieten. Wer die gleichen Namen wie in diesem Tutorial benutzt sollten un "WCF_Tut_Host.HostFunctions" in dem Eingabefenster stehen haben.
Im nächsten Fenster wählen wir den Kontrakt aus. In diesem Fenster sollte man das Interface auswählen, falls man dafür eines erstellt hat. Wir haben das erst einmal gelassen und können hier auch unsere HostFunctions-Klasse eintragen.
Anschließend wählen wir als Kommunikationsart HTTP. Das ist fürs erste am einfachsten. Das Ganze bei der Interoperabilität belassen wir bei Basic.
Nun kommen wir zum eigentlichen Endpunkt unseres Services. Hier kann alles mögliche Eingetragen werden. Wir haben allerdings einen HTTP-Dienst gewählt, deshalb müssen wir auch das HTTP-Protokoll verwenden. Ich habe mir für folgenden Endpunkt entschieden.

http://localhost:8500/MyService

Da wir das ganze auf unserem eigenen Rechner ausprobieren wollen ist als Ziel-IP die Loopback (localhost oder 127.0.0.1) zu wählen. Nachdem alle Einstellungen bestätigt wurden sollte noch eine kurze Zusammenfassung erscheinen. Die daraus gewonnene App.config sieht folgendermaßen aus:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="WCF_Tut_Host.HostFunctions">
                <endpoint address="http://localhost:8500/MyService" binding="basicHttpBinding"
                    bindingConfiguration="" contract="WCF_Tut_Host.HostFunctions" />
            </service>
        </services>
    </system.serviceModel>
</configuration>
Wir sind nun mit dem Host fertig und können ihn erstellen und starten. Wer Windows Vista oder 7 benutzt muss das Programm allerdings als Administrator ausführen. Dafür am besten gleich Visual Studio als Administrator ausführen.
Unser Host kann allerdings noch ein klein wenig verbessert werden. Die Clients, die auf den Host zugreifen, können das zwar nun tun, sie wissen allerdings nicht welche Funktionen unser Host zu Verfügung stellt. Der Host kann dafür Metainformationen zu Verfügung stellen aus dennen die Clients die Schnittstelle auslesen können. Das würde unsere Arbeit mit den Clients erheblich vereinfachen.
Um die Metainformationen über den Service preiszugeben müssen wir noch einmal unsere App.config mit dem Dienstconfigurationseditor öffnen. Wir wählen unter Advanced Service Behaviors aus und erstellen einen neuen Behavior. Diesem Behavior fügen wir ein neues Element vom Typ serviceMetadata hinzu. Ich hab den Service dabei gleich in MyMetaInformations umbenannt. Anschließend können wir das Element bearbeiten. Wir wählen hier HttpGetEnabled aus und erstellen eine Adresse, von der die Metadaten gelesen werden können.
Ich habe mich für http://localhost:8500/MetaInfo entschieden.


Nachdem das geschehen ist können wir unseren Behavior an unseren Service zuweisen. Das geschieht indem wir unseren Service markieren und dort unter BehaviorConfiguration unseren Behavior auswählen.


Nun sind wir mit unserem Host fertig. Nachdem der Host gestartet wurde können wir unter http://localhost:8500/MetaInfo in unserem Browser die Metainformationen auslesen und wissen nun wie der Service anzusprechen ist. Die App.config sieht in der finalen Version des Hostes folgendermaßen aus:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="MyMetaInformations">
                    <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8500/MetaInfo" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="MyMetaInformations" name="WCF_Tut_Host.HostFunctions">
                <endpoint address="http://localhost:8500/MyService" binding="basicHttpBinding"
                    bindingConfiguration="" contract="WCF_Tut_Host.HostFunctions" />
            </service>
        </services>
    </system.serviceModel>
</configuration>
Kommen wir nun zum Client. Wie gesagt können wir dank benutztem HTTP-Protokoll unseren Browser benutzen um die Metainformationen zu holen und daraus unseren Client an den Host anzupassen. Das ganze ist allerdings ziemlich kompliziert. Wir brauchen eine Proxy-Klasse die wir ansprechen um auf die Funktionen des Service zugreifen zu können und auch der Endpunkt muss bekannt sein. Dafür brauchen wir auch im Client eine App.config. Das ganze können wir zum Glück mit einem Tool lösen. Mit svcutil ist es möglich die Metainformationen gleich in eine Konfigurationsdatei und eine passende Klasse zu packen.
Wir starten svcutil über die Eingabeaufforderung von Visual Studio. Diese ist unter Start -> Microsoft Visual Studio -> Visual Studio Tools zu finden. Bevor wir allerdings damit starten können müssen wir noch unseren Host starten, damit dieser die Metainformationen über den Dienst bereitstellen kann.
Mit der Eingabe von svcutil http://localhost:8500/MetaInfo /out:Proxy.cs /config:App.config
werden die beiden benötigten Dateien erstellt. Diese beiden Dateien kopieren wir nun in unser Projekt vom Client. Die Proxy.cs stellt alles bereit was wir benötigen.
In der Mainfunktion des Clients erstellen wir eine Instanz von Proxy und sprechen dieses an wie jede andere Klasseninstanz auch. Im Hintergrund wird das ganze allerdings über den Host geleitet, der uns die Informationen liefert.

//Programm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WCF_Tut_Client
{
  class Program
  {
    static void Main(string[] args)
    {
      using (HostFunctionsClient proxy = new HostFunctionsClient())
      {
        Console.WriteLine( "Bei unserem Host ist es: " + proxy.getDate() + " " + proxy.getTime());
        Console.ReadLine();
      }
    }
  }
}
Wir lesen hier den Wochentag und das Datum vom Host aus und lassen es anzeigen. Nicht gerade spannend, aber für einen Anfang reicht es. Wir können nun zuerst den Host und anschließend den Client starten um unser Projekt zu testen. Wenn alles richtig ist, sollte die folgende Ausgabe erscheinen.


Falls ihr euren Host von einem anderen Computer aus starten wollt um euer Programm auf Netzwerkfähigkeit zu testen müsst ihr nur in der App.config vom Client die neue IP eintragen. Diese ist im Tag endpoint unter address zu finden. Dort einfach Localhost mit der gewünschten IP austauschen.

Anmerken möchte ich noch, dass das svcutil nicht unbedingt gebraucht wird. Man kann sich die Informationen auch über einen Dienstverweis abrufen können. Dies geschieht nachdem der Host gestartet wurde. Man wählt hierfür sein Projekt mit einem Rechtsklick aus und wählt anschließend Dienstverweis hinzufügen. In dem darauf folgenden Dialog muss nur noch die URL mit den Metainformationen angegeben werden. Alles andere wird automatisch für den Benutzer erstellt. Der Nachteil dieser Methode ist die Fehlende App.config, die nun nicht mehr nach belieben Verändert werden kann.
Bookmark and Share

38 Kommentare:

Anonym hat gesagt…

Hi,
wie starte ich denn den Host und den Client gleichzeitig?
Gruß

bebug hat gesagt…

Hallo,
nachdem du einen der beiden gestartet hast gehst du in visual studio zurück, öffnest den Projektmappen-Explorer Strg+Alt+L und klickst mit einem Rechtsklick auf das zweite Projekt. Dort wählst du Debuggen->Neue Instanz starten.

Alternativ kannst du das zweite Projekt auch über den Windows-Explorer starten.

Anonym hat gesagt…

Hallo, danke erstmal für das gute Tutorial
endlich mal was, womit man anfangen kann.

Am Anfang muss man ne Datei namens "Host Funktions" hinzufügen.

Ich nehme an, du meisnt damit ne Klasse^^
wenn ich das so mache und das reinschreibe:
[ServiceContract()]
[OperationContract]
[OperationContract]

meckert er....was ist das? kommentar??

Anonym hat gesagt…

ups....entschuldige, das sind die Attribute
meine IDE hat Sie am Anfang irgendwie nicht erkannt.

Anonym hat gesagt…

Ich finde leider den Dienstconfigurationseditor
nicht.
Wenn ich auf VisualStudio2005/Extras, dann seh im Menü nichts mit WCF-Dienstconf...
Und Unter Windows/SDK/Tools finde ich unter Programmen auch nicht...
Wie finde ich das den?

Anonym hat gesagt…

ahh ich habs, diesen Dienst findet man unter folgendem Pfad
C:\Programme\Microsoft SDKs\Windows\v6.0A\bin\SvcConfigEditor.exe

Anonym hat gesagt…

also ich habe jetzt nicht über das Tool den proxy erstellt, sondern einfach einen dienstverweis auf die url der veröffentlichen MetaDaten.
Er erkennt die 2 Methoden getTime() und getDate()

allerdings, wenn ich dann anfange das so zu implementieren, startet Client die Referenz auf den Hoster und dann kommt: "diese url ist schon regestriert"
Die Fehlermeldung kommt dann in dem Clientfenster.

bebug hat gesagt…

Ja richtig, du kannst natürlich auch direkt einen Dienstverweis erstellen. Der Code von "Program.cs" sieht jedoch identisch aus. Das einzige was hinzukommt ist eine using-Direktive auf deinen Dienstverweis.

Standard: using WCF_Tut_Client.ServiceReference1;

Anonym hat gesagt…

bei mir sieht Client code genauso wie bei dir nur mit einem Unterschied und zwar:

Dienstverweis.HostFunctionsClient objekt = new HostFunctionsClient;

objekt.getData();


Ich starte zuerst den Hoster. Er öffnet die Verbindung und lauscht....

Dann starte ich den Client und geht komischeweise nicht den Client-Code, sondern den Hostercode durch und wenn er auf "open"-Funktion kommt, sagt er mir, dass
HTTP.SYS die url schon regestriert hat....
Ist ja auch nicht verwunderlich.....denn, Hoster läuft ja schon

Er geht irgendwie gar net in den Clientcode rein...

Meine Starteinstellungen: Hoster startet selbst
und wenn er läuft, starte ich den Client selbst manuell

BP hat gesagt…

Super Artikel, hat mir sehr geholfen. Das offizielle Tutorial von MS ist deutlich schlechter. Ich glaube einige der Bilderlinks zu imageshak sind tot. Aber nicht der Rede Wert, man kommt, sofern man sich bereits etwas mit der Materie beschäftigt hat, super aus mit dem text alleine.

Flo hat gesagt…

Danke für den Hinweis. Ich habe die Bilder nun wieder hochgeladen.

Daniel hat gesagt…

Vielen Dank.

Ich hoffe das hilft unseren Usern auch.
http://www.silverlight-community.de

André hat gesagt…

was muss ich beachten, damit ich über netzwerkcomputer kommunizieren kann? den Hostnamen beim appconig angeben.. port freigeben bei firewall, und was noch?? funktioniert bei mir nicht..

Flo hat gesagt…

Es genügt eigentlich die Endpoint-Adresse im Client zu ändern, also nicht im Host. Dann müsste es funktionieren.

endpoint address="net.tcp://192.1.2.50:8500/MyHostService"

Reygh hat gesagt…

Wenn ich jetzt im Host Funktionen hinzufüge/änder/lösche wird ja die Proxy.cs nicht aktualisiert. Wie mache ich das? Oder muss ich das per Hand machen bzw. die Proxy.cs neu erstellen?

Flo hat gesagt…

Hallo Reygh,
die Proxy.cs muss in dem Fall neu erstellt werden. Falls du (anstelle der Proxy.cs) einen Dienstverweis erstellt hast muss dieser auch aktualisiert werden. Dazu ist jedoch nur ein Klick nötig.

Anonym hat gesagt…

Tolles Turorial! Danke, dass Du uns an Deinen Erfahrungen teihaben lässt. Bei mir hat alles wunderbar geklappt.

Konradius hat gesagt…

Besten Dank für dein Tutorial

Anonym hat gesagt…

Quadratisch, praktisch - gut! Herzlichen Dank, hat mir einen schnellen Einstieg ermöglicht.

Anonym hat gesagt…

Ein Super Tutorial

Vielen Dank!

Matthias Maag hat gesagt…

Ich habe folgendes vor: Ich starte eine Instanz meines Programms. Es wird überprüft ob schon ein Host im Netzwerk existiert. Wenn ja, rufe ich ab, welche Instanzen meines Programms im Netzwerk schon existieren. Wenn nein, wird die erste gestartete Instanz zum Host und alle späteren Instanzen melden sich dann dort an. Kann ich den Host also auch zur Laufzeit konfigurieren? Ich doch 2 AppConfigs vorliegen haben und jenachdem ob mein Programm zum Host oder zum Client wird, die eine oder die andere config-Datei verwenden. Oder habe ich da jetzt noch was grundsätzlich falsch verstanden?

Matthias Maag hat gesagt…

Edit: Ich müsste doch 2 Appconfigs...

Flo hat gesagt…

Hallo,
in diesem Fall würde ich die Konfiguration direkt per Code durchführen. Das ganze ist jedoch etwas kompliziert, da die Objekte nicht genau den Element-Namen in der Konfigurationsdatei entsprechen.
Eine Einführung gibt es hier.

Matthias hat gesagt…

Perfekt. Genau das habe ich gemeint. Und mit WCF-Discovery kann ich meinen Dienst-Endpunkt nun auch zur Laufzeit suchen. Man braucht nur die richtigen Stichwörter ;-)

Anonym hat gesagt…

Hi,
dein Tutorial ist richtig klasse, einfach und nachvollziehbar. Danke dir.
Ich habe momentan noch das Problem, dass ich beim Erstellen eines Dienstverweises die Meldung bekomme: 'http://localhost:8500/MetaInfo'.
Die Verbindung mit dem Remoteserver kann nicht hergestellt werden.
Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte 127.0.0.1:8500
Fehler beim Herunterladen von "http://localhost:8500/MetaInf ...
Hast du da eine Idee für mich. Ich arbeite mit Windows 7.

Flo hat gesagt…

Hallo,
im Fall von Win7 musst du die Programme als Administrator starten. Das ist nötig um einen Port öffnen zu können. Am einfachsten wird das realisiert indem Visual Studio als Administrator ausgeführt wird.

Anonym hat gesagt…

Zum obigen "Fehler". Sorry, war nur ein Rechteproblem.
Danke für das tolle Tutorial.

Anonym hat gesagt…

Danke.

Anonym hat gesagt…

Jetzt bin ich's leider nochmal.
Wenn ich via Dienstverweis arbeiten möchte, dann bekomme ich auch die Meldung: Fehler beim Downloaden von 'http://localhost:8500/MyService'.
Die Verbindung mit dem Remoteserver kann nicht hergestellt werden.
Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte 127.0.0.1:8500
Fehler beim Herunterladen von "http://localhost:8500/MyService ...
Ist das auch ein Sicherheitsproblem?
LG

Anonym hat gesagt…

Habe die HostKomponente erstellt. Wen ich diese per Debugger starte, wartet er (lauscht :-)). Doch wenn ich die compilierte exe starte, bekomme ich die Abbruchmeldeung "WcfHost" funktioniert nicht mehr. Woran kann das liegen?
LG

Anonym hat gesagt…

AbbruchMeldung: "WcfHost"->
Wenn ichnach dem Abbruch über die Fehlerbehqandlung gehe und den Debugger aufrufe erhalte ich folgende genauere Fehlermeldung:

----------------------
Der Dienst "WcfHost.WcfHostFunctions" verfügt über keine Anwendungsendpunkte (keine Infrastrukturendpunkte). Dies kann folgende Ursachen haben: Es wurde keine Konfigurationsdatei für die Anwendung gefunden, in der Konfigurationsdatei wurde kein mit dem Dienstnamen übereinstimmendes Dienstelement gefunden oder im Dienstelement wurden keine Endpunkte definiert.
--------------------------
LG

Anonym hat gesagt…

Sehr gutes Tutorial.
Hat bei mir ohne Probleme funktioniert!
Ich musste nur eine Weile suchen, bis ich die svcutil.exe gefunden hatte :).

Anonym hat gesagt…

Danke! Hat auf Anhieb funktioniert!

skdi hat gesagt…

Hilfe
Ich stolpere schon über die einfachsten Sachen. Beim Ausführen der Zeile:
using (ServiceHost sh = new ServiceHost(typeof(HostFunctions)))

bekomme ich eine Fehlermeldung:"Das ' '-Zeichen, hexidezimaler Wert 0x20, darf nicht in einem Namen enthalten sein."
Habe die Dateien nach der Anleitung auf gebaut (erst kopiert und dann nochmal selber geschrieben) immer das gleiche Ergebnis :S Ich habe kein Zeichen 0x20 verwendet???

Vielleicht mache ich ja irgendwo einen grundlegenden Fehler. Ich habe bis jetzt hauptsächlich mit vb.net gearbeiten

Florian Buchner hat gesagt…

Hallo skdi,

0x20 ist das Leerzeichen. Meiner Meinung nach sieht es so aus, als hättest du irgendwo in deiner App.config ein Leerzeichen in einer Zeichenkette.

skdi hat gesagt…

Hallo, vielen Dank!
Das war einfach ;) Ich habe überall gesucht nur dort nicht :S (Leerzeiche war klar aber App.config nicht!)

Sehr schön erklärt, hat jetzt alles funktioniert und ich habe das Prinzip verstanden :))
Danke

Mr. Y hat gesagt…

Hallo, muchos muchos gracias!

Nachdem ich mich mit dem IIS unter Windows 8 fast totgequält habe, hat mich diese Alternativlösung echt gerettet.

Danke für deine Mühe!

Anonym hat gesagt…

Hallo,
hat soweit geklappt. Zuerst bin ich daran gescheitert, dass der "WCF Service Configuration Editor" (unter "Tools") nicht den ServiceType auswählen liess. Die Lösung war, dass man das Projekt auf "Any CPU" bauen muss (habe einen Win7 Rechner). Sonst klappt es nicht.

Kommentar veröffentlichen