2010-10-28 9 views
2

La question résume à peu près tout. J'ai un service WCF, et je veux attendre jusqu'à la fin pour faire autre chose, mais ça doit être jusqu'à la fin. Mon code ressemble à ceci. Merci!Comment puis-je attendre la fin d'un service WCF asynchrone?

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

     GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService"); 

     geocodeService.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted); 

     // Make the geocode request 
     geocodeService.GeocodeAsync(geocodeRequest); 

     //if (geocodeResponse.Results.Length > 0) 
     // results = String.Format("Latitude: {0}\nLongitude: {1}", 
     //  geocodeResponse.Results[0].Locations[0].Latitude, 
     //  geocodeResponse.Results[0].Locations[0].Longitude); 
     //else 
     // results = "No Results Found"; 

     // wait for the request to finish here, so I can do something else 
     // 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: Remove reason later 
      MessageBox.Show("Unable to find address. Reason: " + ex.Message); 
     } 
    } 
+0

Pourquoi ne pas placer ce code dans le gestionnaire d'événements 'GeocodeCompleted'? – Oded

+0

Hmmm n'a pas pensé à ça. Je vais voir comment ça fonctionne. Merci. – Carlo

+0

Utilisez-vous le service avec SilverLight? SilverLight prend uniquement en charge async. –

Répondre

1

Vous pouvez utiliser un ManualResetEvent:

private ManualResetEvent _wait = new ManualResetEvent(false); 

private void RequestGeoCoordinateFromAddress(string address) 
{ 
    ... 
    _wait = new ManualResetEvent(false); 
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes 
    _wait.WaitOne(TimeSpan.FromMinutes(2)); 
    // at that point the web service returned 
} 

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

Évidemment, cela fait n'a absolument aucun sens, la question est donc ici: pourquoi avez-vous besoin de faire cela? Pourquoi utiliser l'appel asynchrone si vous allez bloquer le thread principal? Pourquoi ne pas utiliser un appel direct à la place?

En règle générale, lors de l'utilisation d'appels de service Web asynchrones, vous ne devez pas bloquer le thread principal mais effectuer tout le travail de gestion des résultats dans le rappel asynchrone. En fonction du type d'application (WinForms, WPF), vous ne devez pas oublier que les contrôles de l'interface graphique ne peuvent être mis à jour que sur le thread principal. Si vous avez l'intention de modifier l'interface graphique, vous devez utiliser la technique appropriée.).

+0

+1 n'a aucun sens - totalement d'accord –

+0

Le fait est que le service WCF semble n'avoir que des méthodes asynchrones (GeocodeAsync()), donc je semble être bloqué de manière asynchrone, c'est la raison pour laquelle je veux pour finir avant que je puisse avancer. Je l'essaie dans l'événement Completed et je vois si ça marche à partir de là. – Carlo

+1

Btw, le ManualResetEvent n'a pas fonctionné, il attend d'abord les 2 minutes, puis appelle l'événement Completed. – Carlo

3

Il y a un modèle, soutenu par WCF, pour un appel d'avoir une asynchrone commencent appel, et un appel final correspondant.

Dans ce cas, les méthodes asynchrones serait dans l'interface du client comme si:

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

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

Si vous générez l'interface client à l'aide svcutil avec l'asynchrone l'option appelle, vous obtiendrez tout cela automatiquement . Vous pouvez également créer manuellement l'interface client si vous n'utilisez pas automatiquement les proxys clients.

L'appel de fin se bloquerait jusqu'à ce que l'appel soit terminé.

IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null); 
// 
// Do something else with your CPU cycles here, if you want to 
// 

var geocodeResponse = geocodeService.EndGeocode(asyncResult); 

Je ne sais pas ce que vous avez fait avec vos déclarations d'interface pour obtenir la fonction GeocodeAsync, mais si vous pouvez disputer de nouveau dans ce modèle votre travail serait plus facile.

+0

Je suis d'accord, beaucoup plus simple que d'utiliser un ManualResetEvent. Vous n'êtes pas assuré que le service Web a renvoyé un résultat en utilisant une réinitialisation manuelle, mais vous êtes avec l'appel de fin. – Moderator71

+0

Ohh j'aime vraiment ça. Je vais vous donner un coup de feu. Merci! – Carlo

+0

Hmm seulement dans ce cas, GeocodeAsync renvoie void. – Carlo

1

Ne pas utiliser ce code avec Silverlight:

private ManualResetEvent _wait = new ManualResetEvent(false); 

private void RequestGeoCoordinateFromAddress(string address) 
{ 
    ... 
    _wait = new ManualResetEvent(false); 
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes 
    _wait.WaitOne(TimeSpan.FromMinutes(2)); 
    // at that point the web service returned 
} 

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

Lorsque nous appelons _wait.WaitOne (TimeSpan.FromMinutes (2)), nous bloquons le fil de l'interface utilisateur, ce qui signifie que l'appel de service n'a jamais lieu . En arrière-plan, l'appel à geocodeService.GeocodeAsync est placé dans une file d'attente de messages et ne sera activé que si le thread n'exécute pas de code utilisateur. Si nous bloquons le thread, l'appel de service n'a jamais lieu.

Synchronous Web Service Calls with Silverlight: Dispelling the async-only myth

0

J'ai vu un gars a utilisé ManualReset et waitAll, mais il a dû emballer tout le code à l'intérieur de ThreadPool .. C'est une très mauvaise idée ...pensait que cela fonctionnait