How should I handle async API calls using a CMS that does not allow async controller actions?

≡放荡痞女 提交于 2019-12-22 11:27:04

问题


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!


回答1:


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.




回答2:


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));



回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!