dythim
dythim

Reputation: 930

Async WCF service call blocking UI thread

We are having trouble with a background call from our WPF application. Seemingly, on older computers (we have seen it most often on Windows 7 machines with 2GB of RAM or so) our web service call blocks the UI until it completes. For one call in particular, that is a huge problem because it can take five minutes to complete. Newer computers seem to handle it fine.

We don't care about the amount of time the call takes; we only care about it not blocking the UI. I do not see anything we are doing wrong off-hand.

Are we doing something wrong, or is it something with Windows 7 on 2GB of RAM? Is theere any way to work around it on such a machine?

Do we have to go to the level of writing a custom TaskScheduler to make sure the UI thread does not get used? I sure hope not.

Any input at all is greatly appreciated. Thanks in advance. See code samples below.

DownloadBusinessEntityAsync is the method our application calls:

    #region DownloadBusinessEntity
    public async Task<BusinessEntity> DownloadBusinessEntityAsync(string businessEnityId)
    {
        BusinessEntity ret = new BusinessEntity();

        var client = new DownloadContext();

        try
        {
            Func<AsyncCallback, object, IAsyncResult> begin = (callback, state) => client.BeginDownloadBusinessEntity(businessEnityId, callback, state);
            Func<IAsyncResult, BusinessEntity> end = client.EndDownloadBusinessEntity;

            // ***THIS TAKES FIVE MINUTES TO FINISH***
            ret = await Task<BusinessEntity>.Factory.FromAsync(begin, end, null);

            if (client.State != CommunicationState.Faulted)
                client.Close();
            else
                client.Abort();
        }
        catch (Exception ex)
        {
            client.Abort();
        }

        return ret;
    }
    #endregion

DownloadContext is the WCF client:

public partial class DownloadContext : ClientBase<IDownloadService>, IDownloadService, IDownloadContext, IDisposable
{
    public BusinessEntity DownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes)
    {
        return base.Channel.DownloadBusinessEntity(agencyId, activeHashCodes);
    }

    public IAsyncResult BeginDownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes, AsyncCallback callback, object asyncState)
    {
        return base.Channel.BeginDownloadBusinessEntity(agencyId, activeHashCodes, callback, asyncState);
    }

    public BusinessEntity EndDownloadBusinessEntity(IAsyncResult result)
    {
        return base.Channel.EndDownloadBusinessEntity(result);
    }
}

IDownloadService is the contract the WCF client implements.

[ServiceContract(Namespace = "http://namespace.com/services/v1")]
public partial interface IDownloadService
{
    [OperationContract(ProtectionLevel=ProtectionLevel.Sign, Action="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntity", ReplyAction="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntityResponse")]
    BusinessEntity DownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes);

    [OperationContract(AsyncPattern=true, ProtectionLevel=ProtectionLevel.Sign, Action="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntity", ReplyAction="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntityResponse")]
    IAsyncResult BeginDownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes, AsyncCallback callback, object asyncState);

    BusinessEntity EndDownloadBusinessEntity(IAsyncResult result);

}

Upvotes: 1

Views: 979

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456717

There is a known problem with Microsoft's implementation of client-side HTTP connections (HttpWebRequest and friends): they do some work synchronously, including DNS resolution and (I believe) proxy detection. This work is always done synchronously even for an asynchronous request, and it may take some time depending on your network configuration.

I'm not sure if the WCF clients share this bug, but it's likely. The best workaround is to wrap it in a Task.Run as per usr's answer.

Upvotes: 2

usr
usr

Reputation: 171218

If you simply don't want to block the UI then move that work to a background thread. All that async WCF stuff is not needed to achieve your goal. Something like:

await Task.Run(() => DownloadBusinessEntityAsync(...));

And you can make DownloadBusinessEntityAsync sync if you like.

Upvotes: 1

Related Questions