Reputation: 5933
I have the nuget package Esri.ArcGISRuntime and I need to call the method QueryTask.ExecuteAsync in one of my Web API 2 controllers. There is no synchronous counter part so in my library c# code I am using a wrapper like
private QueryResult ExecuteSync()
{
var queryResults = ExecuteAsync();
queryResults.Wait();
return queryResults.Result;
}
private async Task<QueryResult> ExecuteQueryTaskAsync()
{
var queryTask = new QueryTask(_uri);
return await queryTask.ExecuteAsync(_query).ConfigureAwait(false);
}
Which works perfectly in my program/service. But using ExecuteSync
this way in a Web API 2 controller causes it to completely freeze up and never return a response.
I have done some research and believe the culprit is mentioned here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
I absolutely do not want to use the function asynchronously. The above functions are so core and hidden deep within like 4 wrappers, that it would be a major overhaul of my library class to bubble up async methods just to support this one web api call.
I am looking for work-arounds/hacks/suggestions around this weird behavior of web API to allow me to run this async method synchronously and not have it deadlock
Upvotes: 6
Views: 4328
Reputation: 456507
I absolutely do not want to use the function asynchronously.
I have to say it, though. Asynchronous code is the best solution. The operation you're doing is asynchronous, and exposing a synchronous API for it is problematic at best.
Does it take time? Sure. But your code will be better off for it.
I am looking for work-arounds/hacks/suggestions
I have an entire article on the subject of brownfield async development, where I cover all the known hacks along with their drawbacks.
In your particular case (calling from WebApi on non-Core ASP.NET and considering that it does work from a Console/Win32Service-style app), I'd say the Thread Pool Hack should work for you. It looks like this:
private QueryResult ExecuteSync()
{
return Task.Run(() => ExecuteAsync()).GetAwaiter().GetResult();
}
The idea is that ExecuteAsync
is run on a thread pool thread, outside of the request context. The request thread is then blocked until the asynchronous work completes.
Upvotes: 15
Reputation:
The object you are calling .Wait() on is also a task, and you have not configured it. Add .ConfigureAwait(false) to the return value from ExecuteQueryAsync.
Or, better yet, WebAPI can be a sync as well, so making the whole stack async will be better overall.
Upvotes: 0