使用Await和Async并行运行特定任务

时间:2016-03-01 13:10:08

标签: c# linq asynchronous async-await telerik-grid

当我尝试对特定任务进行并行处理时,需要花费大量时间才能完成。也就是ForEach in Linq。请考虑以下代码,

代码

public async Task<List<int>> ReturnRoutAsync(int UserId, int Id)
{
    return await Task.FromResult(db.RoutingListMembers.Where(a => a.RoutingListId == UserId && a.User.UserDeptId == Id).Select(a => a.UserId.Value).ToList());
}
public async Task<List<int?>> CareAllocCount(List<UserWithDeptId> RouteList)
{
    return await Task.FromResult(RouteList.Where(f => f.PoReceiptId == f.PoReceiptId).Select(f => f.UserId).ToList());
}
public async Task<int> UsersCount(int? UserId)
{
    return await Task.FromResult(db.RoutingListMembers.Where(x => x.RoutingListId == UserId).Select(x => x.UserId).Count());
}

public async Task<List<UserWithDeptId>> GetRoutingList(List<UserWithDeptId> RouteList, int Id)
{
    await RouteList.ForEachAsync(async d => { d.RouteUserIdList = await ReturnRoutAsync(d.UserId.Value, Id); d.CareAllocationCnt = await CareAllocCount(RouteList); d.TotRouteUsers = await UsersCount(d.UserId); });
    return RouteList;
}

public async Task<decimal> ReturnAmount(int Id, int Year)
{
    .... (Some other Logic) ....
    var RouteList = Model.Where(asd => asd.Year == Year).ToList();
    RouteList = await GetRoutingList(RouteList, Id);
    .... (Other logic here)....
    var Amount = NewModelList.Sum(asd => asd.Allocated_Cost_to_Dept); // returns decimal
    return Amount;
}

[GridAction]
public ActionResult _Index(int Year, int? page)
{
      var model = (from Dept in db.UserDepts
                     select new ReviewHead
                     {
                         Header = Dept.Description,
                         DeptId = Dept.Id,
                     }).ToList();
        List<ReviewHead> NewList = new List<ReviewHead>();
        foreach (var temp in model)
        {
            ReviewHead RH = new ReviewHead();

            RH.Amount = ReturnAmount(temp.DeptId, Year); // I am getting the compiler error saying that it cannot convert Task<decimal> to decimal.
            RH.DeptId = temp.DeptId;
            RH.Header = temp.Header;
            NewList.Add(RH);
        }
        return View(new GridModel(NewList));
}

public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
{
    foreach (var value in list)
    {
        await func(value);
    }
}

当我将上面的代码更改为使用Task<ActionResult>时,如下面的代码那样,我收到服务器错误,因为该过程无法继续&#39;异步&#39;。任何帮助将受到高度赞赏。

更新 - 我确实更改了下面的ActionResult方法

[GridAction]
public async Task<ActionResult> _Index(int Year, int? page)
{
      var model = (from Dept in db.UserDepts
                     select new ReviewHead
                     {
                         Header = Dept.Description,
                         DeptId = Dept.Id,
                     }).ToList();
        List<ReviewHead> NewList = new List<ReviewHead>();
        foreach (var temp in model)
        {
            ReviewHead RH = new ReviewHead();

            RH.Amount = await ReturnAmount(temp.DeptId, Year);
            RH.DeptId = temp.DeptId;
            RH.Header = temp.Header;
            NewList.Add(RH);
        }
        return View(new GridModel(NewList));
}

运行时错误

System.InvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The asynchronous action method '_Index' returns a Task, which cannot be executed synchronously.
Source=System.Web.Mvc
StackTrace:
   at   System.Web.Mvc.Async.TaskAsyncActionDescriptor.Execute(ControllerContext   controllerContext, IDictionary`2 parameters)
   at  System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at Vibrant.Controllers.BaseController.ExecuteCore() in e:\Vibrant3_SVN_Latest\Vibrant\Vibrant\Controllers\BaseController.cs:line 42
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.Controller.<>c__DisplayClass19.<BeginExecute>b__13()
   at System.Web.Mvc.Async.AsyncResultWrapper.<.cctor>b__0(IAsyncResult asyncResult, Action action)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
InnerException: 

1 个答案:

答案 0 :(得分:2)

查看异常:

  

异步操作方法'_Index'返回一个无法同步执行的Task。

听起来有些东西无法处理异步代码。正常的MVC操作适用于async,所以它必须是其他的东西。像这样:

  

V:中的Vibrant.Controllers.BaseController.ExecuteCore():Vibrant3_SVN_Latest \ Vibrant \ Vibrant \ Controllers \ BaseController.cs:第42行

听起来像“充满活力” - 无论是什么 - 都需要更新才能启用异步请求。

与此同时,您必须使代码同步。这不是太难,因为代码实际上已经是同步的。

特别是这种方法:

public async Task<List<int>> ReturnRoutAsync(int UserId, int Id)
{
  return await Task.FromResult(db.RoutingListMembers.Where(a => a.RoutingListId == UserId && a.User.UserDeptId == Id).Select(a => a.UserId.Value).ToList());
}

有一个不必要的async / await,它只是打开一个任务然后将结果包装回任务中,所以可以删除它们:

public Task<List<int>> ReturnRoutAsync(int UserId, int Id)
{
  return Task.FromResult(db.RoutingListMembers.Where(a => a.RoutingListId == UserId && a.User.UserDeptId == Id).Select(a => a.UserId.Value).ToList());
}

此外,Task.FromResult同步操作。根本没有关于这个方法的异步,所以我们可以让它同步。因为无论如何它是同步的,同步表示更自然:

public List<int> ReturnRout(int UserId, int Id)
{
  return db.RoutingListMembers.Where(a => a.RoutingListId == UserId && a.User.UserDeptId == Id).Select(a => a.UserId.Value).ToList();
}

在对方法执行此操作后,您的逻辑就是:

public decimal ReturnAmount(int Id, int Year)
{
  .... (Some other Logic) ....
  var RouteList = Model.Where(asd => asd.Year == Year).ToList();
  RouteList = GetRoutingList(RouteList, Id);
  .... (Other logic here)....
  var Amount = NewModelList.Sum(asd => asd.Allocated_Cost_to_Dept); // returns decimal
  return Amount;
}

_Index与第一个示例相同。