2009-02-03 6 views
1

Je tente de refactoriser un code .NET existant modérément complexe pour qu'il soit utilisable dans Silverlight. Le problème principal est que tous les appels de services Web Silverlight doivent être asynchrones et que le code existant a été implémenté de manière synchrone assez typique.Implémentation d'un scénario asynchrone imbriqué "call call" dans .NET

pile d'appels existants pour une opération typique pourrait être quelque chose comme ceci:

page -> GetPersonMethod -> PersonBusinessObject -> PersonDataObject -> CallWebservice -> (réponse à bulles sauvegarde de la pile)

Ma pensée est de diviser toutes les méthodes en méthodes distinctes de demande et de réponse et de préserver la structure globale de l'application.

alors la nouvelle pile d'appel pour la même opération comme ceci:

page -> GetPersonRequest -> PersonBusinessRequest -> PersonDataRequest -> WebserviceRequest

page < - GetPersonResponse < - PersonBusinessResponse < - PersonDataResponse < - WebserviceResponse

questions fondamentales:

  1. Est-ce une idée terrible, et devrais-je vraiment juste le réécrire à partir de zéro avec une perspective plus asynchrone?

  2. En supposant que je prenne cette approche, comment puis-je préserver la pile d'appels pour mes réponses imbriquées?

TIA

-Eric

Répondre

2
  1. Vous ne pouvez pas bloquer le thread principal Silverlight ou votre interface utilisateur se bloque. C'est la raison pour laquelle toutes les opérations réseau dans SL sont forcées asynchrones.
  2. Vous ne pouvez pas toucher l'interface utilisateur à partir d'un thread autre que le thread d'interface utilisateur.

Ce sont les deux contraintes. La façon dont j'aborderais ceci est de créer une fonction "wrapper asynchrone" pour envelopper pour vous. Il faudrait 3 fonctions (délégués): 1. Pour exécuter sur un nouveau thread ("f") (Assurez-vous de ne capturer aucun objet UI dans votre fonction!) 2. Pour exécuter sur exception ("econt" 3. Pour exécuter sur complete ("cont")

Les deux suites seraient distribuées sur le thread d'interface utilisateur via System.Windows.Deployment.Current.Dispatcher.BeginInvoke. Avec cela, vous devez simplement modifier vos appels de service Web pour qu'ils soient "sync via async", comme le dit mbeckish (utilisez un ManualResetEvent, WaitOne du thread de synchronisation, Set on the callback).

Le code pour l'aide pourrait ressembler à ceci (psuedocode, ne pas vérifier):

static void AsyncHelp<T>(Func<T> f, Action<Exception> econt, Action<T> cont) { 
    var t = new Thread((_) => { 
    try { 
     var res = f(); 
     System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => cont(res)); 
    } catch (Exception ex) { 
     System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => econt(ex)); 
    } 
    }); 
    t.Start(); 
} 

Vous finiriez l'utiliser comme ceci:

some_handler() { 
    var id = mytextbox.Text; // Save from UI to local 
    AsyncHelp( 
    () => GetBla(id), 
    bla => result.Text = bla.ToString(), // This is safe cause it's dispatched 
    ex => error.Text = ex.ToString() 
); 
} 

Mise à jour Pour faire l'appel sync-via-asynchrone, vous devriez faire quelque chose comme ceci, en supposant que vous utilisez le "Modèle asynchrone basé sur l'événement" par défaut (utiliser BeginXXX/EndXXX est plus facile, IMO).

Exception ex; 
Result r; 
var mre = new ManualResetEvent(false); 
myService.OnGetBlaCompleted += (_, e) => { 
    ex = e.Error; 
    r = e.Result; 
    mre.Set(); 
} 
myService.GetBlaAsync(id); 
mre.WaitOne(); 
if (ex != null) throw ex; 
// and so on 
+0

Ceci est impressionnant chunck de code, je l'ai eu travailler dans une application de test en un rien de temps! Très utile! Merci! Mais je n'ai pas encore compris comment faire pour que votre proposition de "synchronisation via async" fonctionne pour les appels de services Web asynchrones ... Ma compréhension est que WaitOne() n'est pas supporté par Silverlight? – Scrappydog

+0

http://msdn.microsoft.com/en-us/library/cc190477(VS.95).aspx Semble indiquer WaitOne est là. – MichaelGG

0

Pour éviter refactoring, vous pourriez être en mesure de mettre en œuvre CallWebService comme méthode synchrone qui utilise en interne la demande asynchrone/réponse:

  1. Après avoir fait la requête asynchrone, faites un WaitOne() sur un ManualResetEvent.
  2. Dans le rappel, effectuez Set() sur ManualResetEvent.

Voir http://msdn.microsoft.com/en-us/library/system.net.webrequest.endgetrequeststream.aspx

+0

Une certaine saveur de ceci est théoriquement possible, mais j'ai lu que c'est une mauvaise idée de faire au fait que les applications Silverlight fonctionnent sur un seul fil dans le navigateur ... opinions? – Scrappydog

+0

Si vous bloquez le thread principal Silverlight, l'interface utilisateur va se bloquer. – MichaelGG

+0

Que ce soit SilverLight, WinForms ou ASP.NET, le thread de l'interface utilisateur d'un utilisateur va se bloquer s'il est bloqué. Je dis juste que si le blocage du thread de l'interface utilisateur fonctionnait dans votre paradigme précédent, alors il pourrait être utile de l'essayer ici. – mbeckish

Questions connexes