2009-09-13 6 views
0

J'ai la situation suivante: Mon service WCF permet à un client de s'enregistrer pour attendre un événement. L'attente est asynchrone du côté service, c'est-à-dire que le serveur est enregistré et lorsque le processus est terminé, le serveur est averti. En ce moment, c'est simplement un ManualResetEvent.Async End <Method> non appelé dans WCF

Maintenant, je veux exposer cette méthode via WCF. J'ai essayé d'utiliser AsyncPattern=true et créé deux méthodes, BeginWait qui regroupe l'événement dans un IAsyncResult, et EndWait qui appelle AsyncWaitHandle.WaitOne(). Toutefois, si j'appelle BeginWait, EndWait à partir du client, le côté serveur EndWait n'est pas exécuté. J'utilise un wrapper implémenté manuellement (ma classe proxy est dérivée de ChannelBase<IWaitService>, IWaitService), qui appelle essentiellement Channel.EndWait(), et cette fonction est en effet appelée; mais côté serveur, l'appel n'arrive jamais.

Qu'est-ce que je fais mal ici? Question de suivi: Si l'appel asynchrone fonctionne, y a-t-il un moyen facile de le rendre synchrone du côté client?

+1

Vous pouvez vérifier sur http: //blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx surtout pour savoir que sync-versus-async est une chose 'locale' (vous pouvez appeler une méthode sync ou async du client, et qui pourrait être mis en œuvre sync ou async sur le serveur, les deux n'ont rien à voir avec un autre). – Brian

+0

Fondamentalement, ce billet de blog l'a résolu - pourriez-vous faire une réponse à partir de votre commentaire, donc je peux l'accepter? – Anteru

Répondre

0

La décision synchrone/asynchrone peut être prise indépendamment du côté du serveur ou du client. L'appel de EndWait sur le client ne se traduit pas par un appel EndWait sur le serveur. Je recommande de tester un service async avec un client de synchronisation juste pour garder les choses simples et éviter la confusion.

Je vous recommande en outre de ne pas appeler WaitOne à l'intérieur de la méthode EndWait. Le contrat est que cette méthode ne sera appelée qu'après que IAsyncResult indique au framework que c'est fait. Cela se fait dans l'une des trois façons suivantes:

  • completedSynchronously retourne true
  • Le AsyncCallback est invoqué
  • Le AsyncWaitHandle est signalé

completedSynchronously ne devrait retourner vrai si BeginWait avait suffisamment d'informations pour terminer la demande avant son retour. Ce n'est probablement pas le cas. Vous pouvez satisfaire les deux autres conditions avec votre ManualResetEvent comme suit:

class EventBasedAsyncResult : IAsyncResult 
{ 
    private readonly ManualResetEvent _manualResetEvent; 
    private readonly AsyncCallback _asyncCallback; 
    private readonly object _asyncState; 

    public EventBasedAsyncResult(AsyncCallback callback, object asyncState) 
    { 
     _manualResetEvent = new ManualResetEvent(false); 
     _asyncState = asyncState; 
     _asyncCallback = callback; 
    } 

    public void WaitCompleted() 
    { 
     _manualResetEvent.Set(); 
     _asyncCallback(this); 
    } 

    public object AsyncState 
    { 
     get { return _asyncState; } 
    } 

    public WaitHandle AsyncWaitHandle 
    { 
     get { return _manualResetEvent; } 
    } 

    public bool CompletedSynchronously 
    { 
     get { return false; } 
    } 

    public bool IsCompleted 
    { 
     get { return _manualResetEvent.WaitOne(0); } 
    } 
} 

Je pense qu'une fois que vous faites cela, vous verrez que EndWait est appelée, même si le client est synchrone.

2

La ligne

var task = Task.Factory.StartNew(() => IsPrime(a)); 

utilise surcharge

TaskFactory.StartNew(Action) 

qui se traduit par

((IAsyncResult)task).AsyncState == null 

l'appel de rappel automatique (tâche) entraîne une ArgumentException, se plaindre que l'objet d'état est différent de l'objet d'état qui a été transmis à la méthode BeginXxx.La ligne doit être modifiée pour

var task = Task.Factory.StartNew((actionState) => IsPrime(a), state); 

en utilisant la surcharge

TaskFactory.StartNew(Action<object>, object) 

de telle sorte que l'objet d'état passé par la WCF se termine dans la tâche:

((IAsyncResult)task).AsyncState.GetType().FullName == System.ServiceModel.Dispatcher.MessageRpc+Wrapper 
Questions connexes