C# webapi中wait-async的正确用法

C# webapi中wait-async的正确用法,c#,asp.net-mvc-4,asp.net-web-api,.net-4.5,async-await,C#,Asp.net Mvc 4,Asp.net Web Api,.net 4.5,Async Await,我有一个WebApi,它可以为每个传入的请求调用2个单独的WebService,执行一些后期处理并返回结果 第一个webservice调用在本地缓存1小时,其中的数据决定对第二个webservice的查询。在每个传入请求上调用第二个Web服务。在发出第二个请求之后,将使用业务逻辑处理每个结果,并将其返回给客户机响应 对第二Web服务的调用不能是异步的,因为它使用的第三方dll不允许使用wait关键字。我所做的是将第二次webservice调用和后期处理打包成一个异步函数,该函数由控制器调用 //

我有一个WebApi,它可以为每个传入的请求调用2个单独的WebService,执行一些后期处理并返回结果

第一个webservice调用在本地缓存1小时,其中的数据决定对第二个webservice的查询。在每个传入请求上调用第二个Web服务。在发出第二个请求之后,将使用业务逻辑处理每个结果,并将其返回给客户机响应

对第二Web服务的调用不能是异步的,因为它使用的第三方dll不允许使用wait关键字。我所做的是将第二次webservice调用和后期处理打包成一个异步函数,该函数由控制器调用

// /api/controller/news?key=a&state=b
public async Task<HttpResponseMessage> GetNews(string key, string state)
    {
         // call to first webservice if not in cache
         if (JsonConfig != null && JsonConfig.Configuration.NewsQuery.ContainsKey(key))
        { 
            var results = await SearchProxyProvider.Search(filters.All, filters.Any, filters.None, filters.Sort, 100, 0, true, state, true);
            int totalCount = results.TotalCount;

            return Request.CreateResponse(HttpStatusCode.OK, results);
        }
    }


// Helper class method
public async Task<ItemCollection<Item>> Search(List<FieldValuePair> allFilters, List<FieldValuePair> anyFilters, List<FieldValuePair> noneFilters, SortedFieldDictionary sortBy, int pageSize = 100, int pageNumber = 0, bool exact = true, string stateFilter = null, bool getAllResults = true)
        {
            // call to 2nd api
            search = SomeApi.Search(allFilters, anyFilters, noneFilters, pageSize, pageNumber, exact,
                                               sortBy, null, WebApiConstant.Settings.CustomFields, true);

            // post processing on search results
            return search;
        }
///api/controller/news?key=a&state=b
公共异步任务GetNews(字符串键、字符串状态)
{
//如果不在缓存中,则调用第一个webservice
if(JsonConfig!=null&&JsonConfig.Configuration.NewsQuery.ContainsKey(键))
{ 
var results=await SearchProxyProvider.Search(filters.All、filters.Any、filters.None、filters.Sort、100、0、true、state、true);
int totalCount=results.totalCount;
返回Request.CreateResponse(HttpStatusCode.OK,results);
}
}
//助手类方法
公共异步任务搜索(列出所有筛选器、列出所有筛选器、列出非筛选器、SortedFieldDictionary sortBy、int pageSize=100、int pageNumber=0、bool exact=true、string stateFilter=null、bool getAllResults=true)
{
//调用第二个api
search=SomeApi.search(所有过滤器、任意过滤器、非过滤器、页面大小、页码、精确、,
sortBy,null,WebApiConstant.Settings.CustomFields,true);
//搜索结果的后处理
返回搜索;
}
因为对第一个webservice的调用是在本地缓存的,所以我并不认为异步调用有什么好处


我只是想看看这种方法是完全错误的,还是正确的。

要使函数搜索真正异步调用SomeApi,可以将搜索包装到单独的任务中,然后等待。还可以考虑将第一个函数中的代码封装到一个任务中,您的负载估计缓存验证也可能是一个瓶颈。
// call to 2nd api
var search = await Task.Factory.StartNew(()=> {return Some Api.Search(allFilters, anyFilters, noneFilters, pageSize, pageNumber, exact,
                                               sortBy, null, WebApiConstant.Settings.CustomFields, true);});
第一个webservice调用在本地缓存1小时,其中的数据决定对第二个webservice的查询

您可以使用
AsyncLazy
(例如)执行一些技巧,并将其缓存。这为您的请求提供了一种(异步)等待刷新的方式,并且在需要刷新数据时,无论同时请求的数量如何,您都不会多次点击Service1

对第二Web服务的调用不能是异步的,因为它使用的第三方dll不允许使用wait关键字

那真是个倒霉蛋。一定要依靠他们来解决它。:)

我所做的是将第二次webservice调用和后期处理打包成一个异步函数,该函数由控制器调用

// /api/controller/news?key=a&state=b
public async Task<HttpResponseMessage> GetNews(string key, string state)
    {
         // call to first webservice if not in cache
         if (JsonConfig != null && JsonConfig.Configuration.NewsQuery.ContainsKey(key))
        { 
            var results = await SearchProxyProvider.Search(filters.All, filters.Any, filters.None, filters.Sort, 100, 0, true, state, true);
            int totalCount = results.TotalCount;

            return Request.CreateResponse(HttpStatusCode.OK, results);
        }
    }


// Helper class method
public async Task<ItemCollection<Item>> Search(List<FieldValuePair> allFilters, List<FieldValuePair> anyFilters, List<FieldValuePair> noneFilters, SortedFieldDictionary sortBy, int pageSize = 100, int pageNumber = 0, bool exact = true, string stateFilter = null, bool getAllResults = true)
        {
            // call to 2nd api
            search = SomeApi.Search(allFilters, anyFilters, noneFilters, pageSize, pageNumber, exact,
                                               sortBy, null, WebApiConstant.Settings.CustomFields, true);

            // post processing on search results
            return search;
        }
那没有意义。编译器将警告您,您的“异步”方法实际上是同步的

如果它是同步的,那么就同步调用它。在服务器端,没有必要将其包装到
任务中。运行
或类似的操作


我有一些星期一晚上给你的,你可能会觉得有用。(他们有关于如何处理异步请求以及为什么“假异步”方法在服务器端没有帮助的动画)。

您平均每小时需要多少次web api调用?查看1000个并发用户。将有多个服务器负载平衡webapi,2-3。最初的应用程序下载预计将有400K用户。实际上,这是“假异步”,而不是“真正异步”,而且您肯定不想在服务器端(WebAPI)上这样做。顺便说一句,
Task.Run
StartNew
更适合
async
代码。那么对于服务器端webapi,什么是真正异步的呢?我使用术语“真正异步”来表示不将工作排队到线程池的异步方法。在服务器端,
async
的整个要点是释放工作线程,如果您通过将工作排队到线程池(由另一个工作线程执行)来释放工作线程,那么您将一无所获。OTOH,对于客户端上的
async
,这是一种非常有用的技术,因为关键是释放UI线程。因此,您可以将工作排队到线程池,这很好,因为它不是在UI线程上完成的。@StephenCleary感谢您的反馈。在我评论“伪异步”之前,我将查看您提到的幻灯片。关于你对Task.Run的推荐,我认为如果你提到Task.Run会更好。它只是Task.Factory.StartNew的一个较短的替代品。“更好”这个词太笼统了。@亚历克斯:我通常使用的措辞是“
Task.Run
TaskFactory更受欢迎。对于
async
code..,StartNew
”,但“更好”并不是不正确的。
StartNew
的问题在于它很容易被错误地使用,例如忘记了
TaskScheduler
参数(应该始终指定它-一些细节在Stephen Toub的博客文章中)。因此,如果我的web调用都不能异步调用,但我有一个方法可以执行长时间运行的任务,如何使其异步?(服务器端WebAPI)@mickyjtwin:你不应该。您可以将其包装在
任务中。运行
,但这不会给您带来任何好处。WebAPI中的
async
的全部要点是减少线程数量,因此您根本不想将工作排队到线程池。如果这个解释不清楚,那么想想这个问题的答案:为什么我要使它异步?你能给我指出一些关于这方面的详细书籍的方向吗?我真的很想深入了解正在发生的事情以及