如何使用不允许异步控制器操作的CMS处理异步API调用?

时间:2016-06-06 12:17:25

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

我一直在使用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;
}

在其他人中使用async一直到控制器。我正在使用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 });
}

根据我的研究,这些解决方案都不理想,虽然我没有遇到任何死锁问题,但一切似乎都有效。有人可以帮我理解我应该遵循的最佳模式并解释原因吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

  

我使用的是SiteFinity,无法使用异步控制器操作

According to SiteFinity,它确实支持异步控制器操作,而不支持小部件中的异步操作。如果这是您的情况,我建议您vote

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

如果您不想丢失已经在async中投入的投资,那么我建议您使用&#34;布尔参数hack&#34;正如我在brownfield async的文章中所述。

答案 1 :(得分:1)

您永远不应该只在异步方法上调用Result,因为您可能会在应用程序中导致死锁。我没有使用SiteFinity,但确实在MVC中大量使用子操作,而且那些也不允许异步响应。我的做法是使用我从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));

答案 2 :(得分:0)

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

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

或者你可以在任务完成之前返回,然后每隔几秒钟继续进行一次轮询,询问“我们还在吗?”,这看起来相当极端,除非绝对必要,否则我不会这样做。