2010-03-28 7 views
1

Le code suivant contient quelques appels asynchrones imbriqués dans certaines boucles foreach. Je sais que les appels silverlight/wcf sont appelés de manière asynchrone, mais comment puis-je m'assurer que mes objets wcfPhotographers, wcfCategories et wcfCategories sont prêts avant le début de la boucle foreach? Je suis sûr que je vais à ce sujet tout à fait faux-et j'apprécierais une aide que vous pourriez donner.Comment utiliser les appels Async (WCF) imbriqués dans les boucles foreach dans Silverlight?

private void PopulateControl() 
    { 

     List<CustomPhotographer> PhotographerList = new List<CustomPhotographer>(); 

     proxy.GetPhotographerNamesCompleted += proxy_GetPhotographerNamesCompleted; 
     proxy.GetPhotographerNamesAsync(); 


     //for each photographer 
     foreach (var eachPhotographer in wcfPhotographers) 
     { 

      CustomPhotographer thisPhotographer = new CustomPhotographer(); 

      thisPhotographer.PhotographerName = eachPhotographer.ContactName; 
      thisPhotographer.PhotographerId = eachPhotographer.PhotographerID; 

      thisPhotographer.Categories = new List<CustomCategory>(); 

      proxy.GetCategoryNamesFilteredByPhotographerCompleted += proxy_GetCategoryNamesFilteredByPhotographerCompleted; 
      proxy.GetCategoryNamesFilteredByPhotographerAsync(thisPhotographer.PhotographerId); 


      // for each category 
      foreach (var eachCatergory in wcfCategories) 
      { 

       CustomCategory thisCategory = new CustomCategory(); 

       thisCategory.CategoryName = eachCatergory.CategoryName; 
       thisCategory.CategoryId = eachCatergory.CategoryID; 

       thisCategory.SubCategories = new List<CustomSubCategory>(); 

       proxy.GetSubCategoryNamesFilteredByCategoryCompleted += proxy_GetSubCategoryNamesFilteredByCategoryCompleted; 
       proxy.GetSubCategoryNamesFilteredByCategoryAsync(thisPhotographer.PhotographerId,thisCategory.CategoryId); 

       // for each subcategory 
       foreach(var eachSubCatergory in wcfSubCategories) 
       { 
        CustomSubCategory thisSubCatergory = new CustomSubCategory(); 

        thisSubCatergory.SubCategoryName = eachSubCatergory.SubCategoryName; 
        thisSubCatergory.SubCategoryId = eachSubCatergory.SubCategoryID; 
       } 


       thisPhotographer.Categories.Add(thisCategory); 
      } 

      PhotographerList.Add(thisPhotographer); 
     } 

     PhotographerNames.ItemsSource = PhotographerList; 
    } 




    void proxy_GetPhotographerNamesCompleted(object sender, GetPhotographerNamesCompletedEventArgs e) 
    { 
     wcfPhotographers = e.Result.ToList(); 
    } 


    void proxy_GetCategoryNamesFilteredByPhotographerCompleted(object sender, GetCategoryNamesFilteredByPhotographerCompletedEventArgs e) 
    { 
     wcfCategories = e.Result.ToList(); 
    } 

    void proxy_GetSubCategoryNamesFilteredByCategoryCompleted(object sender, GetSubCategoryNamesFilteredByCategoryCompletedEventArgs e) 
    { 
     wcfSubCategories = e.Result.ToList(); 
    } 

Répondre

3

Oui, avant de pouvoir procéder à l'étape suivante de l'algorithme, vous devez avoir obtenu le résultat de l'étape précédente, ce qui peut être difficile lorsque vous devez utiliser les méthodes asynchrones.

Si cela ne se produit pas sur le thread d'interface utilisateur, vous pouvez simplement bloquer et attendre la réponse. Par exemple, avoir chaque signal de méthode "terminé" (en utilisant les primitives de synchronisation disponibles dans Silverlight, je ne sais pas par exemple si ManualResetEvent est là, si oui, avoir l'appel de rappel terminé .Set()), puis avoir votre méthode principale PopulateControl appelez l'appel FooAsync(), puis bloquez jusqu'à ce que les signaux ManualResetEvent (en appelant .Wait()).

Si cela se trouve sur le thread de l'interface utilisateur et que vous avez vraiment besoin d'écrire une solution non bloquante, il est beaucoup plus difficile de le coder correctement en C#. Vous pouvez envisager d'utiliser F # à la place, où async fournit un modèle de programmation agréable pour les appels non bloquants.

EDIT:

exemple pseudo-code pour bloquer les résultats:

// class-level 
ManualResetEvent mre = new ManualResetEvent(false); 
// some method that needs to make WCF call and use results 
void Blah() { 
    // yadda yadda 
    proxy.FooCompleted += (o,ea) => { ... mre.Set(); }; 
    proxy.FooAsync(...); 
    mre.WaitOne(); // block until FooCompleted 
    // use results from FooCompleted now that they're here 
    // mre.Reset() if you intend to use it again later 
} 

J'ai utilisé un lambda pour FooCompleted, mais en utilisant une méthode distincte comme vous avez est bien aussi.

+0

merci pour votre réponse -pourriez-vous donner un exemple de pseudo-code sur la façon dont j'implémenterais le .Set() et .Wait() acclamations –

+0

pas qui ne fonctionne pas unfortunaelty -il se bloque sur mre.Wait(); –

+0

Notez que si vous l'utilisez plus d'une fois, vous devrez .Reset() entre les appels. – Brian

0

Vous pouvez également, pour chaque méthode asynchrone que vous utilisez pour remplir la collection, créer une méthode d'assistance qui renvoie IObservable, puis utiliser la requête Linq pour regrouper le résultat.

Par exemple:

private IObservable<Photographer> GetPhotographerNames() 
{ 
    var photographers = Observable 
     .FromEvent<GetPhotographerNamesCompletedEventArgs>(proxy, "GetPhotographerNamesCompleted") 
     .Prune() 
     .SelectMany(e => e.EventArgs.Result.ToObservable()); 

    proxy.GetPhotographerNamesAsync(); 

    return photographers; 
} 

Et de même:

private IObservable<Category> GetCategoryNamesFilteredByPhotographer(int photographerId)  { ... } 
private IObservable<SubCategory> GetSubCategoryNamesFilteredByCategory(int photographerId, int categoryId) { ... } 

Vous pouvez maintenant écrire une requête Linq:

var pcs = from p in GetPhotographerNames() 
      from c in GetCategoryNamesFilteredByPhotographer(p.PhotographerId) 
      from s in GetSubCategoryNamesFilteredByCategory(p.PhotographerId, c.CategoryId) 
      select new {p, c, s}; 

Cette requête vous renvoie une liste de triplets (Photographe, Category, SubCategory) Maintenant, tout ce que vous avez à faire est de vous y abonner et de l'agréger aux objets que vous utilisez sur e client qui devrait être assez simple.

+0

Salut merci pour suggestion -quel cadre/Silverlight version vise-t-il? –

+0

Fonctionne dans SL3 mais nécessite le téléchargement de Reactive Extensions (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) –