I have been getting my feet wet using async/await and most of what I have been reading states "Don't Block on Async Code" with the exception of the main function in a console application. I find myself unable to follow this simple rule :(
My application depends heavily on a WebAPI layer. The client I am using in the MVC apllication is using the async methods on System.Net.Http.HttpClient
to call API methods. Currently I am handling it two ways. In some places we are getting the result immediately after calling the client:
//WebAPI Client
public object GetSomething(int id)
{
var task = _client_GetSomethingFromApiAsync(id);
var result = task.Result;
return result;
}
In others using async all the way up to the controller. I am using SiteFinity and cannot use async controller actions so I end up with something like this:
//WebAPI Client
public Task<object> GetSomethingAsync(int id)
{
return _client_GetSomethingFromApiAsync(id);
}
//Domain Service
public Task<object> GetSomethingDomainServiceAsync(int id)
{
return _service.GetSomethingAsync(id);
}
//Controller
public JsonResult GetSomethingControllerAction(int id)
{
var result = _domainService.GetSomethingDomainServiceAsync(viewModel.id).Result;
return Json(new { Success = true, Payload = result });
}
Based on my research both of these solutions are not ideal although I have not had any issues with deadlocks and everything seems to be working. Can someone please help me understand the best pattern I should follow and explain why?
Thank You!
I am using SiteFinity and cannot use async controller actions
According to SiteFinity, it does support async controller actions, just not async actions in widgets. If this is your situation, I encourage you to vote.
Assuming that you're in this situation - and cannot use async
code - then I recommend that you just use synchronous code all the way. That is, replace, HttpClient
with WebClient
or something like that. I do not recommend using Result
since it is possible (unlikely, but possible) that future updates to HttpClient
may cause deadlocks even if your code does not deadlock today.
If you don't want to lose the investment you've already made in async
, then I'd recommend using the "boolean argument hack" as described in my article on brownfield async.
You should never just call Result
on an async method, as you can cause deadlocks in your application. I don't use SiteFinity, but do utilize child actions heavily in MVC and those also don't allow async responses. My course of action has been to use a helper class that I stole from the Entity Framework codebase.
Entity Framework 6 added async methods in addition to the original sync methods, but behind the scenes, only the async methods do any actual work. The sync methods now just call the async methods synchronously. To do this, they implemented an internal helper class called AsyncHelper
(internal visibility, so you can't just use it in your own application). However, I took the code and created my own AsyncHelper
in my application:
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return AsyncHelper._myTaskFactory
.StartNew<Task<TResult>>(func)
.Unwrap<TResult>()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
AsyncHelper._myTaskFactory
.StartNew<Task>(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
To use this, in your controller action, you would just do:
var something = AsyncHelper.RunSync<SomeType>(() => _client_GetSomethingFromApiAsync(id));
As I understand it, an async controller action just saves you a thread. It has no other effect on the behavior of the website.
So without async controllers, you will have to wait until the Task has been completed (blocking the thread, so it cannot be used for another request).
Or you could return before the Task has completed, and then keep polling every few seconds asking "are we there yet?", which seems fairly extreme and I wouldn't do that unless absolutely necessary.
来源:https://stackoverflow.com/questions/37657257/how-should-i-handle-async-api-calls-using-a-cms-that-does-not-allow-async-contro