in Dev

Clientseitige asynchrone Serviceaufrufe mit der ChannelFactory

Wenn eine WPF Applikation entfernte Methoden ausführen will (Interprozesskommunikation), sollten diese wenn immer möglich asynchron ausgeführt werden. Wir müssen immer davon ausgehen, dass auch scheinbar kurze Funktionen für einmal auch mal länger dauern können. Zudem werden die Aufrufe in einer WPF Applikation standardmässig im UI Thread ausgeführt. Das  führt dazu, dass die Benutzeroberfläche in der Zwischenzeit für den Benutzer einfrieren kann (‘Keine Rückmeldung’ wird in der Titelleiste angezeigt.)

Mit der WCF gibt es verschiedene Möglichkeiten die Aufrufe asynchron umzusetzen.

  • Mit dem generierten Client-Proxy via svcutil mit dem Parameter /async
    Bspw: svcutil *.wsdl *xsd /l:c# /out: FlennyNET.LearingWCF.ClientProxy.cs /async
  • Über die Funktion Add Service Reference in den erweiterten Einstellungen das Kontrollkästchen generate asynchronous operations anwählen.
  • Oder die Funktion serverseitig mit dem AsyncPattern implementieren.

Ich konnte mit den automatisch generierten Proxies noch nie etwas anfangen, weshalb ich in meinen Projekten oft die ChannelFactory einsetze. Sämtliche Service- und Datenverträge lagere ich in eine zusätzliche Assembly aus, so dass Client und auch Server darauf zugreifen können. Die ChannelFactory bietet mir aber leider keine asynchrone Methoden der Schnittstelle an. Die Asynchronizität kann nur erreicht werden, wenn für jeden Aufruf künstlich einen Thread hergestellt wird.

Ich bin ein fauler Programmierer und möchte mich weder auf der Server- noch auf der Clientseite um irgendwelche Arbeiten, die mir schlussendlich einen simplen asynchronen Methodenaufruf bereitstellen, kümmern müssen. Das sollte doch eigentlich die Aufgabe von der WCF sein!

Die Lösung/ Workaround funktioniert nun wie folgt:

Ich erstelle wie gewohnt ein ServiceContract

[ServiceContract(Name = "DemoContract", Namespace = "https://ping.flenny.net/")]
 public interface IfleDemoContract
 {
     [OperationContract]
     CfleDemoData GetData();
 }

Dabei ist hier bereits wichtig zu beachten, dass für das WSDL einen expliziten Namen angegeben werden muss (bspw. ‘DemoContract’). Auf der Seite des Servers implementieren wir nun dieses Interface normal synchron. Mit dem System.Threading.Thread.Sleep() erzeugen wir eine künstliche Verzögerung.

public class CfleDemoContract : IfleDemoContract
 {
     public CfleDemoData GetData()
     {
         System.Threading.Thread.Sleep(5000);

         return new CfleDemoData {
             Id = 0,
             Content = "Daten vom Server!" };
     }
 }

Wenn wir nun auf Seite des Clients einen neuen Proxy mit der ChannelFactory erzeugen, steht uns nur die synchrone Variante zur Verfügung.

sync1

Damit wir nun auf Seite des Clients die asynchronen Methoden vom WCF Framework zur Verfügung gestellt bekommen, erstellen wir ein zweites Interface, welches die zusätzlichen Methoden für die asynchrone Ausführung gemäss dem AsyncPattern zur Verfügung stellt. Auch hier ist die explizite Namesvergabe für das Interface wieder speziell zu berücksichtigen. Die Namen der beiden Interfaces müssen identisch sein.

[ServiceContract(Name = "DemoContract", Namespace = "https://ping.flenny.net/")]
 public interface IfleDemoContractAsync
 {
     [OperationContract(AsyncPattern = true)]
     IAsyncResult BeginGetData(AsyncCallback callback, object asyncState);
     CfleDemoData EndGetData(IAsyncResult result);
 }

Der Proxy wird dann wie folgt erstellt.

IfleDemoContractAsync objProxy = ChannelFactory.
     CreateChannel(new NetTcpBinding(),
         new EndpointAddress("net.tcp://localhost:9876/flennyNET"));

Danach stehen die asynchronen Methoden zur Verfügung.

async

Der clientseitige notwendige Thread wird nun vom WCF Framework automatisch erstellt.

Mit diesem Vorgehen erreichen wir, dass wir serverseitig normal synchron programmieren können und clientseitig die ChannelFactory verwenden können und uns aber nicht um die Erstellung von Threads kümmern müssen.

Ich hab euch auch hier wieder ein Visual Studio 2010 Demo Projekt erstellt, welches ihr hier herunterladen könnt. Viel Spass!