We are currently implementing the new WCF REST service in IIS for our site, and on several pages we can make multiple AJAX calls using jQuery asynchronously. The problem is that it looks like WCF (server side) is running synchronously.
When loading the page, we make 3 separate calls to 3 different methods. Using logging, I see that they all fall into the global.asax file within about 5 ms of each other. From there, logging shows that everything runs in the order in which they exit global.asax (not necessarily in the order in which we made calls from the page using javascript). I expected each call to receive its own flow and return individually. Even when you attach to the debugger, I see that it will not execute the next method until I move on to the current method.
Below are the operating contracts for the three methods that I thought: "I used to use the asynchronous model.
[OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListUserPreferences" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListUserPreferences(AsyncCallback callback, object state); Result<List<Data.EnumerationItem<UserPreferenceType>>> EndListUserPreferences(IAsyncResult asyncResult); [OperationContract(Name = "GetUserSecure", AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/GetUser" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginGetUser(AsyncCallback callback, object state); Result<Data.User> EndGetUser(IAsyncResult asyncResult); [OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListWithAttributes" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListWithAttributes(int index, int pageSize, AsyncCallback callback, object state); Result<PagedCollection<Data.Attribute>> EndListWithAttributes(IAsyncResult asyncResult);
Here is an example of one of the implementations in the service.
public IAsyncResult BeginGetUser(AsyncCallback callback, object state) { var asyncResult = new CompletedAsyncResult<Result<Data.User>>(state); asyncResult.Result = new Result<Data.User>(); asyncResult.Result.Value.UserId = Guid.Empty; asyncResult.Result.Value.DisplayName = "asdfasd"; asyncResult.IsCompleted = true; callback(asyncResult); return asyncResult; } public Result<Data.User> EndGetUser(IAsyncResult asyncResult) { return ((CompletedAsyncResult<Result<Data.User>>)asyncResult).Result; }
Here are the attributes that we have in the service implementation class.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
Can someone explain why they execute synchronously and what I need to do, or at least point me in the direction of what I need to do to get them to execute asynchronously?
UPDATE
I took part of Matt's answer and shifted my logic to calls to the End function and watched this blog post about how he made it a little closer Downloading large files Self-hosted WCF Recreation Service . However, I could not get End methods to use its technique. I'm starting to think that I'm wrong too. Since I look at the logs, my user authentication service is executed before each service call, which happens before any operations with the asynchronous method even work. After further study, I looked at the ThreadId of each request included in IIS, and then performed the operations. It seems that WCF uses only one worker thread ... period. It doesn't matter how many requests I send to IIS. For example, if I send 3 requests, I see that they all come in at different times (in milliseconds of each other), and everyone gets their own stream. But then it seems that WCF just queues them and runs them in that order, because they all run on the same thread, including authentication service calls.