Bisher habe ich die WCF ausschließlich dazu genutzt, SOAP-Dienste zu implementieren, die ich dann in einer IIS-Umgebung bereitgestellt habe. Letztendlich ist dies aber nur ein spezieller Zweck für den sich die WCF bestens eignet – genau genommen lässt sich die WCF nämlich generell einsetzen, wenn es darum geht seperate Systeme miteinander zu verbinden. Dies können beispielsweise auch Anwendungen sein, die auf einem Computer ausgeführt werden und untereinander – über Prozessgrenzen hinweg – Daten austauschen sollen. Wie eine solche Kommunikation realisiert werden kann, möchte ich hier an einem einfachen Beispiel demonstrieren.  

Am Anfang steht natürlich der Service-Contract…  


[ServiceContract()]
public interface ISampleService
{
    [OperationContract()]
    SampleResponse SampleMethod(SampleRequest r);
}

[DataContract()]
public class SampleRequest
{
    [DataMember()]
    public string Input { get; set; }
}

[DataContract()]
public class SampleResponse
{
    [DataMember()]
    public string Output { get; set; }    
}

Die Service-Contracts halte ich von den Implementierungen getrennt in einem seperaten Assembly. Dies ist in reinen SOAP-Service-Szenarien nicht erforderlich, da Visual Studio beim Hinzufügen einer Service-Reference entsprechende Typen anhand der Metadaten erzeugt. Für Dienste, die für die Kommunikation zwischen Anwendungen konzipiert sind, eignen sich allerdings (auf Grund des geringeren Nachrichten-Overhead) TCP- oder Named-Pipe-Kanäle besser, die jedoch nicht über einen Dienstverweis, sondern programmatisch in die Anwendung eingebunden werden müssen.  

Wenn es nun darum gehen soll, Anwendungen miteinander kommunizieren zu lassen, so muss eine der Anwendungen als Dienst-Anbieter (Host) fungieren - beliebige andere Anwendungen können den Dienst konsumieren – sofern diese den Service-Contract kennen. Die Beispielimplementierung des SampleService sieht so aus…  


public class SampleService : WcfService.Contracts.ISampleService
{
    public SampleService() { }

    public Contracts.SampleResponse SampleMethod(Contracts.SampleRequest r)
    {
        return new Contracts.SampleResponse() { Output = r.Input };
    }
}

Sehr komplex, wie man sieht :-)  

Das folgende Beispiel zeigt eine einfache Konsolen-Anwendung, die diesen WCF-Dienst hostet…  


class Program
{
    static void Main(string[] args)
    {
        Uri serviceAddress = new Uri(@"http://localhost:4711/SampleService");

        ServiceHost host = new ServiceHost(typeof(SampleService), serviceAddress);

        host.Open();

        Console.WriteLine("Service is alive.");

        Console.Read();

        host.Close();
    }
}

Sobald die Anwendung gestartet ist, kann man den Dienst über den Browser aufrufen… allerdings möchte dieser Dienst noch keine Details zu seiner Verwendung offenbaren. Dazu muss ein entsprechendes Behaviour konfiguriert werden, so…  


class Program
{
    static void Main(string[] args)
    {
        Uri serviceAddress = new Uri(@"http://localhost:4711/SampleService"); 

        ServiceHost host = new ServiceHost(typeof(SampleService), serviceAddress); 

        ServiceMetadataBehavior metaDataBehavior = new ServiceMetadataBehavior();
        metaDataBehavior.HttpGetEnabled = true;
        metaDataBehavior.MetadataExporter.PolicyVersion = PolicyVersion.Default;
        host.Description.Behaviors.Add(metaDataBehavior); 

        host.Open();
        Console.WriteLine("Service is alive."); 

        Console.Read(); 

        host.Close();
    }
} 

Dieser Dienst kann problemlos über den “Add Service-Reference”-Dialog in Visual Studio eingebunden werden. Im Folgenden soll es nun jedoch um die Realisierung eines TCP-Bindings gehen. Dazu wird der Code der Host-Anwendung noch einmal erweitert (den Beispielcode habe ich etwas eingekürzt). Bevor der ServiceHost über die Open-Methode geöffnet wird, können weitere Endpunkte mit Hilfe der AddServiceEndpoint-Methode hinzugefügt werden… 

...
  host.AddServiceEndpoint(typeof(ISampleService), new NetTcpBinding(), "net.tcp://localhost:4712/SampleService");
  host.Open();
  Console.WriteLine("Service is alive.");
...

Ob dieser Endpunkt wie gewünscht bereitgestellt wird, kann nicht mehr über den Browser kontrolliert werden. Es ist also an der Zeit sich der Implementierung des Clients zuzuwenden. Dazu habe ich eine weitere Konsolenanwendung erstellt. Diese referenziert das Assembly, das den Service-Contract bereitstellt (wie zuvor beschrieben). Über die ChannelFactory-Klasse kann dann für den Contract ein Proxy generiert werden – quasi “on-the-fly”.

 
static void Main(string[] args)
{
    NetTcpBinding binding = new NetTcpBinding(); 

    ChannelFactory<ISampleService> sampleServiceFactory;
    sampleServiceFactory = new ChannelFactory<ISampleService>(
        binding, "net.tcp://localhost:4712/SampleService");
   
    ISampleService serviceClient = sampleServiceFactory.CreateChannel(); 

    SampleRequest tcpRequest = new SampleRequest() { Input = "Hello World." };
    SampleResponse tcpResponse = serviceClient.SampleMethod(tcpRequest); 

    Console.WriteLine(tcpResponse.Output); 

    Console.Read();
} 

Super einfach, wie ich finde.