(I'm using .Net 4.0)
I want to call a WCF service asynchronously, from my service layer. This service layer is used by and MVC.Net controller. I've read that it's good practice to call a WCF service asynchronously. So I'm using begin/end (apm). I want to double check if I'm doing it richt:
public byte[] GetSomeData()
{
IAsyncResult result = myServiceClient.BeginDoSomething(someInputValue, null, null);
var data = _pdfCreatieService.EndCreateForPreview(result);
return data;
}
I'm not totally sure about the code above, because I've seen constructions like the code below, which seem a bit more complex and unnecessary in my case:
public byte[] GetSomeData()
{
var myState = new MyState();
IAsyncResult result = _myServiceClient.BeginDoSomething(someInputValue, CreateForPreviewCallback, myState);
result.AsyncWaitHandle.WaitOne();
return myState.Bytes;
}
private void DoSomethingCallback(IAsyncResult result)
{
var myState = (MyState)result.AsyncState;
myState.Bytes = _myServiceClient.EndDoSomething(result);
}
Thanks Avner Shahar-Kashtan, Ned Stoyanov and Noseratio. Your answers are really insightfull!
Both of your approaches are doing what is called sync over async. That is executing an asynchronous method in synchronous fashion. A better approach would be to build your own asynchronous method to rerieve the data with
TaskCompletionSource
. I haven't tested this, but you should be able to do something like this:Then to use it simply do this
This code will return straight away and once the asynchronous operation is complete the
ContinueWith
part will execute.In ASP.NET MVC, you only benefit from asynchronous calls if your controller is asynchronous too. Apparently, this is not the case with your code, because you're blocking with
WaitOne
inside your controller's method.Implementing asynchronous controllers is really easy with .NET 4.5, check "Using Asynchronous Methods in ASP.NET MVC 4" for more info.
With .NET 4.0, it's a bit more tedious, check "Using an Asynchronous Controller in ASP.NET MVC". Your controller should derive from
AsyncController
and useAsyncManager
to notify the ASP.NET stack about the pending asynchronous operations.Here's an example from there for .NET 4. 0, adapted for your case(untested). Note the use of
Task.Factory.FromAsync
andTask.ContinueWith
:What your code will do is, in effect, take an asynchronous method and call it synchronously. When you call the
EndDoSomething
method, you are effectively blocking your thread until the asynchronous method has completed, which is exactly the opposite of an async call.Of course, your second code block also calls the code synchronously, by blocking executing explicitly, using the waithandle.
What you want to do is, instead of returning the
byte[]
from your initial method, have yourDoSomethingCallback
do something active with the bytes - either store them in some class member that can be checked by the controller, raise an event, or do something else. If you're waiting for your async call, you're not getting any benefit.What you can also do if you're using .NET 4.5 or higher (or .NET 4.0 in VS2012, using the BCL Async Package) is to use
async/await
, which is a nice wrapper which will allow you to consolidate the calling method and the callback method into a single, more coherent method.But regardless of the syntax or libraries you choose, your first step is to understand that async programming necessarily breaks your code's control flow into the invocation and the result callback, or continuation of the asynchronous operation.