Using asynchronous tasks to call synchronous WCF s

2019-08-28 02:36发布

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

3条回答
孤傲高冷的网名
2楼-- · 2019-08-28 02:49

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.

查看更多
淡お忘
3楼-- · 2019-08-28 02:51

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.

查看更多
聊天终结者
4楼-- · 2019-08-28 03:03

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;
}
查看更多
登录 后发表回答