Anteru
Anteru

Reputation: 19404

Async End<Method> not called in WCF

I have the following situation: My WCF service allows a client to register to wait for some event. The waiting is asynchronous on the service side, that is, the waiter is registered and when the process is finished, the waiter is notified. At the moment, it's simply a ManualResetEvent.

Now I want to expose this method via WCF. I tried to use AsyncPattern=true and created two methods, BeginWait which bundles the event into a IAsyncResult, and EndWait which calls AsyncWaitHandle.WaitOne(). However, if I call BeginWait, EndWait from the client, the server side EndWait is not executed. I'm using a manually implemented wrapper (my proxy class is derived from ChannelBase<IWaitService>, IWaitService), which basically calls Channel.EndWait(), and this function is indeed called; but on the server side, the call never arrives.

What am I doing wrong here? Follow-up question: If the asynchronous call is working, is there an easy way to make that synchronous on the client side?

Upvotes: 0

Views: 2119

Answers (2)

ewj
ewj

Reputation: 21

The line

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

uses overload

TaskFactory.StartNew(Action)

which results in

((IAsyncResult)task).AsyncState == null

the call to callback(task) results in an ArgumentException, complaining that the state object is different from the state object that was passed to the BeginXxx method. The line must be modified to

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

using overload

TaskFactory.StartNew(Action<object>, object)

such that the state object passed by WCF ends up in the task:

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

Upvotes: 2

Michael L Perry
Michael L Perry

Reputation: 7427

The synchronous/asynchronous decision can be made independently on the server or client side. Calling EndWait on the client does not translate into an EndWait call on the server. I would recommend testing an async service with a sync client just to keep things simple and avoid confusion.

I would further recommend that you not call WaitOne inside of the EndWait method. The contract is that this method will only be called after the IAsyncResult tells the framework that it is done. This is done in one of three ways:

  • CompletedSynchronously returns true
  • The AsyncCallback is invoked
  • The AsyncWaitHandle is signalled

CompletedSynchronously should only return true if BeginWait had enough information to complete the request before it returned. This is probably not the case. You can satisfy the other two conditions with your ManualResetEvent as follows:

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); }
    }
}

I think once you do this you'll find that EndWait is called, even if the client is synchronous.

Upvotes: 0

Related Questions