2 Stimmen

Wie kann ich auf einen synchronen WCF-Dienst warten, um abzuschließen?

Die Frage fasst es ziemlich gut zusammen. Ich habe einen WCF-Dienst und möchte warten, bis er fertig ist, um etwas anderes zu tun, aber es muss warten, bis er fertig ist. Mein Code sieht ungefähr so aus. Danke!

    private void RequestGeoCoordinateFromAddress(string address)
    {
        GeocodeRequest geocodeRequest = new GeocodeRequest();

        GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

        geocodeService.GeocodeCompleted += new EventHandler(geocodeService_GeocodeCompleted);

        // Mach die Geocode-Anfrage
        geocodeService.GeocodeAsync(geocodeRequest);

        //if (geocodeResponse.Results.Length > 0)
        //    results = String.Format("Breitengrad: {0}\nLängengrad: {1}",
        //      geocodeResponse.Results[0].Locations[0].Latitude,
        //      geocodeResponse.Results[0].Locations[0].Longitude);
        //else
        //    results = "Keine Ergebnisse gefunden";

        // Warte hier auf das Ende der Anfrage, um etwas anderes tun zu können
        // DoSomethingElse();
    }

    private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
    {
        bool isErrorNull = e.Error == null;
        Exception error = e.Error;

        try
        {
            double altitude = e.Result.Results[0].Locations[0].Latitude;
            double longitude = e.Result.Results[0].Locations[0].Longitude;

            SetMapLocation(new GeoCoordinate(altitude, longitude));
        }
        catch (Exception ex)
        {
            // TODO: Grund später entfernen
            MessageBox.Show("Adresse nicht gefunden. Grund: " + ex.Message);
        }
    }

3voto

Andrew Shepherd Punkte 42283

Es gibt ein Muster, das von WCF unterstützt wird, für einen Aufruf, bei dem ein asynchroner begin Aufruf und ein entsprechender end Aufruf erfolgen.

In diesem Fall wären die asynchronen Methoden in der Schnittstelle des Clients wie folgt:

[ServiceContract]
interface GeocodeService
{
     // Synchrone Operationen
     [OperationContract(AsyncPattern = false, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     GeocodeResults Geocode(GeocodeRequestType geocodeRequest);

     // Asynchrone Operationen
     [OperationContract(AsyncPattern = true, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     IAsyncResult BeginGeocode(GeocodeRequestType geocodeRequest, object asyncState);
     GeocodeResults EndGeocode(IAsyncResult result);
}

Wenn Sie die Client-Schnittstelle mit svcutil und der Option für asynchrone Aufrufe generieren, erhalten Sie all dies automatisch. Sie können auch die Client-Schnittstelle manuell erstellen, wenn Sie die automatische Generierung der Client-Proxys nicht verwenden.

Der End Aufruf würde blockieren, bis der Aufruf abgeschlossen ist.

IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null);
//
// Führen Sie hier etwas anderes mit Ihren CPU-Zyklen aus, wenn Sie möchten
//

var geocodeResponse = geocodeService.EndGeocode(asyncResult); 

Ich weiß nicht, was Sie mit Ihren Schnittstellendeklarationen gemacht haben, um die GeocodeAsync-Funktion zu erhalten, aber wenn Sie es schaffen könnten, es wieder in dieses Muster zu bringen, würde Ihre Arbeit einfacher werden.

1voto

Darin Dimitrov Punkte 990883

Sie könnten ein ManualResetEvent verwenden:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest);
    // warten für maximal 2 Minuten
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // zu diesem Zeitpunkt hat der Webdienst geantwortet
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

Offensichtlich ergibt es überhaupt keinen Sinn, dies zu tun, also lautet die Frage hier: Warum muss das gemacht werden? Warum eine asynchrone Aufruf verwenden, wenn der Hauptthread blockiert wird? Warum nicht stattdessen einen direkten Aufruf verwenden?

Im Allgemeinen sollten Sie den Hauptthread nicht blockieren, wenn Sie asynchrone Webdienstaufrufe verwenden, sondern die gesamte Arbeit zur Handhabung der Ergebnisse im asynchronen Rückruf erledigen. Je nach Art der Anwendung (WinForms, WPF) sollten Sie nicht vergessen, dass GUI-Steuerelemente nur im Hauptthread aktualisiert werden können. Wenn Sie beabsichtigen, das GUI im Rückruf zu ändern, sollten Sie die entsprechende Technik verwenden (InvokeRequired, ...).

1voto

abg Punkte 1003

Verwenden Sie diesen Code nicht mit Silverlight:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest); 
    // warte maximal 2 Minuten
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // zu diesem Zeitpunkt hat der Webdienst geantwortet
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

Wenn wir _wait.WaitOne(TimeSpan.FromMinutes(2)) aufrufen, blockieren wir den UI-Thread, was bedeutet, dass der Serviceaufruf nie stattfindet. Im Hintergrund wird der Aufruf von geocodeService.GeocodeAsync tatsächlich in eine Nachrichtenwarteschlange gestellt und wird nur ausgeführt, wenn der Thread keinen Benutzercode ausführt. Wenn wir den Thread blockieren, findet der Serviceaufruf nie statt.

Synchroner Aufruf von Webdiensten mit Silverlight: Entzauberung des async-only Mythos

0voto

Stefan Forster Punkte 395

Die Visual Studio 11 Beta enthält C# 5 mit async-await.

Siehe Async CTP - Wie kann ich async/await verwenden, um einen WCF-Dienst aufzurufen?

Es ermöglicht das Schreiben von async-Clients in einem 'synchronen Stil'.

0voto

Vitaliy Markitanov Punkte 1846

Ich sah, dass jemand ManualReset und waitAll benutzte, aber er musste den gesamten Code in ThreadPool umhüllen. Es ist eine sehr schlechte Idee... obwohl es funktioniert

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X