We are currently implementing a new WCF REST service in IIS for our site and on a number of pages we may be making a handful of AJAX calls using JQuery asynchronously. The problem is that it seems as though WCF (on the server side) is executing synchronously.
On page load we're making 3 separate calls to 3 different methods. Using logging, I can see them all hit the global.asax file within about 5ms of each other. From there, the logging shows everything executing in the order they exit the global.asax (not necessarily the order in which we made the calls from the page via javascript). I expected each call to receive their own thread and return individually. Even when attaching with the debugger I can see that it won't execute the next method until I step through the current method it's on.
Here are the operation contracts for three of the methods I 'thought' I implemented to use the async 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 we have on the service implementation class.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
Can anyone please provide some insight as to why these are executing synchronously and what I need to do, or at least point me in the direction of what I need to do, to get these to execute asynchronously?
UPDATE
I took some of Matt's answer and moved my logic to the End function calls and followed this blog post on how he did it a bit more closely Uploading Large Files To Self Hosted WCF Rest Service. However, I couldn't get the End methods to call using his technique. I'm also starting to think I'm going about this the wrong way. Because looking at the logs, my custom authentication service is being executed before each service call, which happens before any async method operations even fire. After further investigation, I took a look at the ThreadId's of each request coming into IIS and then executing the operations. It appears that WCF is only using 1 worker thread... period. It doesn't matter how many requests I send at a time to IIS. For example, if I send 3 requests, I can see they all come in at different times (within milliseconds of each other) and all get their own thread. But then it seems WCF just queues them all and executes them in that order because they're all executed on the same thread, including the authentication service calls.