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

穿精又带淫゛_ 提交于 2019-12-06 07:15:27

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.

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