Paul Michaels
Paul Michaels

Reputation: 16685

Using asynchronous tasks to call synchronous WCF service

I have a WCF service, called by a service client. I'd like to use the async / await constructs to wrap the call to this; however, the service and service client are .NET3.5. My solution to this is as follows:

private async Task<ObservableCollection<MyEntity>> LoadData(ParamData param)
{
        ServiceClient svc = new ServiceClient();
        int results = 0;

        // Set-up parameters
        myParams = BuildParams(param);

        // Call a count function to see how much data we're talking about
        // This call should be relatively quick
        var counter = Task.Factory.StartNew(() =>
        {
            results = svc.GetResultCount(myParams);
        }).ContinueWith((task) =>
        {
            if (results <= 10000 ||
                (MessageBox.Show("More than 10000 results, still retrieve data?"), MessageBoxButton.YesNo) == MessageBoxResult .Yes))
            {
                return svc.Search(myParams);
            }
        });
}

I get the compile error:

Since 'System.Action<System.Threading.Tasks.Task>' returns void, a return keyword must not be followed by an object expression

So, my question is, is it possible to run a synchronous method in this fashion and, if so, what am I doing wrong? My objective is that the method can be called like so:

var data = await LoadData(params);

Upvotes: 0

Views: 6129

Answers (3)

Stephen Cleary
Stephen Cleary

Reputation: 456322

In your question, you first state that your client is on .NET 3.5, but then you proceed with an async method and tagged your question .NET 4.5. So I'm assuming that you are actually running on .NET 4.5.

In that case, you can just tell svcutil to create task-based asynchronous APIs (in VS2012, it should do this by default), and then call them like this:

private async Task<ObservableCollection<MyEntity>> LoadData(ParamData param)
{
    ServiceClient svc = new ServiceClient();

    // Set-up parameters
    myParams = BuildParams(param);

    // Call a count function to see how much data we're talking about
    // This call should be relatively quick
    var results = await svc.GetResultCountAsync(myParams);
    if (results <= 10000 ||
        (MessageBox.Show("More than 10000 results, still retrieve data?"), MessageBoxButton.YesNo) == MessageBoxResult .Yes))
        return await svc.Search(myParams);
}

If you are actually on .NET 4.0, then Henk has the correct answer. In that case, you may find my async WCF blog post helpful.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273169

When you add the Service Reference there is an option to generate async versions of the operations.

This is the (older) APM pattern (IAsyncResult, BeginMethod, EndMethod). You can hook this into async/wait with FromAsync :

 var task = Task.Factory.FromAsync(BeginGetResultCount, EndGetResultCount, myParams);

When you have many calls this is better, it doesn't waste so many threads to wait for I/O.

Upvotes: 6

Paul Michaels
Paul Michaels

Reputation: 16685

Okay - I've solved this. The return statement returns from the task, not the function; which is why it was complaining.

The correct code looks like this:

private async Task<ObservableCollection<MyEntity>> LoadData(ParamData param)
{
    ServiceClient svc = new ServiceClient();
    int results = 0;

    // Set-up parameters
    myParams = BuildParams(param);

    // Call a count function to see how much data we're talking about
    // This call should be relatively quick
    var counter = Task.Factory.StartNew(() =>
    {
        results = svc.GetResultCount(myParams);
    });

    var returnTask = counter.ContinueWith((task) =>
    {
        if (results <= 10000 ||
            (MessageBox.Show("More than 10000 results, still retrieve data?"), MessageBoxButton.YesNo) == MessageBoxResult .Yes))
        {
            return svc.Search(myParams);
        }
    });

    return returnTask.Result;
}

Upvotes: 0

Related Questions