【问题标题】:How should I handle async API calls using a CMS that does not allow async controller actions?我应该如何使用不允许异步控制器操作的 CMS 处理异步 API 调用?
【发布时间】:2016-06-06 12:17:25
【问题描述】:

我一直在使用 async/await 让我的脚湿透,我读到的大部分内容都是 “不要阻塞异步代码”,控制台应用程序中的 main 函数除外.我发现自己无法遵循这个简单的规则:(

我的应用程序严重依赖于 WebAPI 层。我在 MVC 应用程序中使用的客户端正在使用 System.Net.Http.HttpClient 上的异步方法来调用 API 方法。目前我正在以两种方式处理它。在某些地方,我们在调用客户端后立即得到结果:

//WebAPI Client
public object GetSomething(int id)
{
    var task = _client_GetSomethingFromApiAsync(id);
    var result = task.Result;
    return result;
}

在其他使用异步一直到控制器的情况下。我正在使用 SiteFinity 并且不能使用异步控制器操作,所以我最终得到了这样的结果:

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

根据我的研究,这两种解决方案都不理想,尽管我没有遇到任何死锁问题,而且一切似乎都正常。有人可以帮我理解我应该遵循的最佳模式并解释原因吗?

谢谢!

【问题讨论】:

    标签: c# asp.net-mvc asynchronous asp.net-web-api sitefinity


    【解决方案1】:

    我正在使用 SiteFinity,无法使用异步控制器操作

    According to SiteFinity,它确实支持异步控制器操作,只是不支持小部件中的异步操作。如果这是你的情况,我鼓励你vote

    假设您处于这种情况 - 并且不能使用 async 代码 - 那么我建议您一直使用 同步 代码。也就是说,将HttpClient 替换为WebClient 或类似的东西。我不建议使用Result,因为即使您的代码今天没有死锁,未来对HttpClient 的更新也有可能(不太可能,但有可能)导致死锁。

    如果您不想失去已经在 async 上进行的投资,那么我建议您使用我在 brownfield async 上的文章中描述的“布尔参数破解”。

    【讨论】:

    • 决定向我的 API 客户端添加异步和同步方法。例如,GetAsync 将使用 HttpClient,Get 将使用 WebClient。
    【解决方案2】:

    您不应该只在异步方法上调用Result,因为您可能会导致应用程序死锁。我不使用 SiteFinity,但确实在 MVC 中大量使用子操作,并且这些操作也不允许异步响应。我的做法是使用从 Entity Framework 代码库中窃取的帮助程序类。

    Entity Framework 6 在原有同步方法的基础上添加了异步方法,但在幕后,只有异步方法做任何实际工作。同步方法现在只是同步调用异步方法。为此,他们实现了一个名为AsyncHelper 的内部帮助器类(内部可见性,因此您不能只在自己的应用程序中使用它)。但是,我获取了代码并在我的应用程序中创建了自己的AsyncHelper

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

    要在您的控制器操作中使用它,您只需:

    var something = AsyncHelper.RunSync<SomeType>(() => _client_GetSomethingFromApiAsync(id));
    

    【讨论】:

      【解决方案3】:

      据我了解,异步控制器操作只是为您节省了一个线程。它对网站的行为没有其他影响。

      因此,如果没有异步控制器,您将不得不等到任务完成(阻塞线程,因此它不能用于其他请求)。

      或者您可以在任务完成之前返回,然后每隔几秒轮询一次,询问“我们到了吗?”,这似乎相当极端,除非绝对必要,否则我不会这样做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-04
        • 2015-08-20
        • 1970-01-01
        • 1970-01-01
        • 2015-01-01
        • 2018-08-05
        相关资源
        最近更新 更多