c# - 异步调用永远不会在 Asp.Net MVC 中返回

标签 c# asp.net .net asp.net-mvc async-await

我返回一个基本上调用两个异步操作的列表:

[HttpPost]
public ActionResult List(DataSourceRequest command, ProductListModel model)
{
    var categories = _productService.GetAllProducts(model.SearchProductName,
        command.Page - 1, command.PageSize);

    var gridModel = new DataSourceResult
    {
        Data = categories.Select(async x =>
        {
            var productModel = x.ToModel();
            var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
            var category = await _categoryService.GetCategoryById(x.CategoryId);

            productModel.Category = category.Name;
            productModel.Manufacturer = manufacturer.Name;
            return productModel;
        }),
        Total = categories.TotalCount
    };
    return Json(gridModel);
}

这是一个 ajax 请求(来自客户端),但在前端永远不会返回。有没有死锁?

最佳答案

根据几条评论和@usr 的回答建立我的答案:

  • Data上面代码中实际上是IEnumerable<Task<ProductModel>> , 不是 IEnumerable<ProductModel> .这是因为 lambda 传递给了 Selectasync .
  • 很可能,JSON 序列化程序遍历此结构并枚举 Task<ProductModel> 上的属性实例,包括 Result .

我在我的博客上解释why accessing Result will cause a deadlock在这种情况下。简而言之,这是因为 async lambda 将在其 await 之后尝试在 ASP.NET 请求上下文上恢复执行.但是,ASP.NET 请求上下文在调用 Result 时被阻止,锁定该请求上下文中的线程,直到 Task<T>完成。自 async lambda 无法恢复,它无法完成该任务。所以这两件事都在等待对方,你会遇到一个典型的死锁。

有一些使用建议await Task.WhenAll ,我通常会同意。但是,在这种情况下,您使用的是 Entity Framework 并收到此错误:

A second operation started on this context before a previous asynchronous operation completed.

这是因为 EF 无法在同一个数据库上下文中同时执行多个异步调用。有几种方法可以解决这个问题;一种是使用多个数据库上下文(本质上是多个连接)来同时进行调用。 IMO 一种更简单的方法是按顺序而不是同时进行异步调用:

[HttpPost]
public async Task<ActionResult> List(DataSourceRequest command, ProductListModel model)
{
  var categories = _productService.GetAllProducts(model.SearchProductName,
      command.Page - 1, command.PageSize);

  var data = new List<ProductModel>();
  foreach (var x in categories)
  {
    var productModel = x.ToModel();
    var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
    var category = await _categoryService.GetCategoryById(x.CategoryId);

    productModel.Category = category.Name;
    productModel.Manufacturer = manufacturer.Name;
    data.Add(productModel);
  }

  var gridModel = new DataSourceResult
  {
    Data = data,
    Total = categories.TotalCount
  };
  return Json(gridModel);
}

关于c# - 异步调用永远不会在 Asp.Net MVC 中返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33185932/

相关文章:

c# - Entity Framework : Generate custom Id for MYSQL table

.net - 在 Visual Studio 中使用快捷方式自动生成代码?

c# - 在C#中将字母字符串转换为整数

c# - 为什么 SortedSet<T>.GetViewBetween 不是 O(log N)?

c# - ASP.NET Core,在用户登录时收到通知

c# - 在 Asp.net mvc 中维护 View 状态?

默认情况下,使用 Azure Active Directory 登录页面托管在 IIS 中的 ASP.NET Web 应用程序

c# - Roslyn 分析器未检测 While 表达式

c# - 是否有可能对当前被 `using` block 消耗的引用进行 GC?

c# - 从 C# 应用程序调试 MySQL 连接问题