9
\$\begingroup\$

I really like using the new TAP pattern in .Net 4.5. and I am updating some of my older projects to use it.

One of my old patterns was to use EAP with WCF so I could have functions that could take longer than 60 seconds (the default timeout for WCF) to complete without doing custom setup on the client side App.Conifg.

Here is a simple example of what I would do.

[ServiceContract(CallbackContract = typeof(ICallback))]
public interface IService1
{
    [OperationContract(IsOneWay = true)]
    void Test(int arg);
}

[ServiceContract]
interface ICallback
{
    [OperationContract(IsOneWay=true)]
    void Callback(int arg);
}

public class Service1 : IService1
{
    public void Test(int arg)
    {
        var callback = OperationContext.Current.GetCallbackChannel<ICallback>();
        Thread.Sleep(61000); //Sleep for 61 sec.
        callback.Callback(arg + 1);
    }
}

Now I want to put a wrapper around my EAP pattern and turn it in to a TAP pattern (as an aside, just returning a TAP pattern like public Task<int> Test(int arg) will still have the 60 second time limit but it does work)

Here is the solution I came up with, this code would be run on the client.

static class ProxyClient
{
    private delegate void CallbackDelegate(int arg);

    private class CallbackClass : IService1Callback
    {
        public event CallbackDelegate CallbackEvent;

        void IService1Callback.Callback(int arg)
        {
            var tmp = CallbackEvent;
            if (tmp != null)
                tmp(arg);
        }
    }

    public static async Task<int> Test(int arg)
    {
        var callback = new CallbackClass();
        var client = new Service1Client(new InstanceContext(callback));
        TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();

        //start listening for the completion event
        callback.CallbackEvent += (resultArg) => tcs.TrySetResult(resultArg);

        client.Test(arg);

        //wait for the result
        var result = await tcs.Task;

        //close the connection
        client.Close();

        return result;
    }
}

The main concern I had was receiving the completion callback for another invocation if a connection was used more than once. So I create and breakdown the connection from inside the function entirely.

I would love to hear input if this is a good idea, or is there a better way to accomplish long running tasks in WCF without having to modify timeout values client-side at the time of proxy creation.

\$\endgroup\$
1
\$\begingroup\$

Design Trigger

I don't understand why you implement a duplex wcf operation to emulate a simplex operation, just to avoid configuring the timeout. Let's assume you have a really good reason why you don't want to change the default timeouts, then your solution can still time out on being idle (after 10 minutes), because you changed from timing out on SendTimeout to ReceiveTimeout.

As explained here the following timeouts have an impact on your code:

  • SendTimeout: maximum threshold to expire an ongoing simplex operation. -> The timeout you don't like to configure (default: 1 minute)
  • ReceiveTimeout: maximum idle time on a session. -> The timeout between the one-way message and the callback fired back. (default: 10 minutes)

Review

  • Your concern about using a single instance for multiple calls is valid. Using a single instance with a single callback is the simplest solution. Otherwise, you would need to implement a matching system with correlation ids in the request and callback contracts.

  • The lifetime management is not robust enough. Use a try-finally block to close the connection. Also, distinguish between Abort (when connection is Faulted) and Close (graceful disconnect) (Close Connection).

    //wait for the result
    var result = await tcs.Task;  // <- if this throws an exception
    
    //close the connection
    client.Close();       // <- this won't be called
    
\$\endgroup\$
  • 1
    \$\begingroup\$ You understand wcf :-o I have never met anybody who whould know how this crap works :-P the first paragraph is like black magic. \$\endgroup\$ – t3chb0t Aug 2 at 21:55
  • 1
    \$\begingroup\$ I used to work on a project where we used duplex tcp connections in wcf. I had to find out the hard way what all the timeouts actually are meant for. \$\endgroup\$ – dfhwze Aug 3 at 7:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.