异步/等待多个Linq语句

时间:2019-04-29 06:15:08

标签: c# .net asp.net-core async-await

我有一个API控制器操作,该操作执行大约10个单独的linq查询,这些查询用于形成我需要发送回客户端的摘要对象。这些linq查询都在同一数据上执行。有没有一种方法可以在这种情况下使用异步/等待,以便一个linq语句不必阻止其他语句运行?如果是这样,编写异步/等待代码的最有效方法是什么?

总结我的问题:

  1. 在这种情况下,我是否有用于异步/等待的用例?
  2. 如果是这样,而不是创建一堆独立的任务,然后将它们全部塞入Task.WhenAll(),有没有更有效的方式来编写此代码,以便以后可以轻松添加更多linq查询? (没有什么太疯狂了,只是干净且易于维护)。
[HttpGet]
public IActionResult GetInventoryDetails(string id)
{
    var inventory = _storeInventoryRepo.FindByCondition(s => s.Id = id)

    var uniqueProductCount = inventory.Select(x => x.ProductId).Distinct().ToList().Count

    var totalProductInventoryValue = inventory.Sum(x =>x.UnitPrice & x.TotalUnits)

    var cheapestProduct = inventory.OrderBy(x => x.unitPrice).Select(x => x.ProductId).First();

    var inventorydetails = new InventoryDetails
    {
       UniqueProductCount = uniqueProductCount,
       TotalProductInventoryValue = totalProductInventoryValue,
       CheapestProduct = cheapestProduct
    }

    Return Ok(inventoryDetails)
}

    public class ProductInventory
    {
        public string Id { get; set; }
        public string ProductId { get; set; }
        public int UnitPrice { get; set; }
        public double TotalUnits { get; set; }
    }

我将如何使用async / await来执行uniqueProductCost,totalProductInventoryValue和CheapestProduct,而无需等待一个完成?

2 个答案:

答案 0 :(得分:0)

由于您使用的是IEnumerable<T>,而不是IQueriable<T>,因此无法使用async-await

除非您使用Task.Run。但是就像在ASP.NET中将async-await与真正的异步API(例如I / O)一起使用一样,您会牺牲性能来提高可用性,而使用Task.Run则会牺牲性能来提高可用性。

如果您使用Select(x => x.ProductId).Distinct()Count(),则与使用Select(x => x.ProductId).Distinct().ToList().Count相比,分配的资源更少,CPU使用的数量也更少。

使用inventory.OrderBy(x => x.unitPrice).First().ProductId代替inventory.OrderBy(x => x.unitPrice).Select(x => x.ProductId).First()也是如此。

答案 1 :(得分:0)

  

在这种情况下,我是否有用于异步/等待的用例?

如果您确定要同时运行这些数据库请求。

请注意,许多数据库客户端仅限于每个连接一个查询-用EF术语,这意味着您需要为每个查询使用单独的数据库上下文。这种额外的复杂性(和开销)可能值得,也可能不值得;您必须确定自己。

  

如果是这样,而不是创建一堆独立的任务,然后将它们全部塞入Task.WhenAll(),有没有更有效的方式编写此代码,以便以后可以轻松添加更多linq查询? (没有什么太疯狂了,只是干净且易于维护)。

Task.WhenAll将是最干净的方法。如果更适合您,可以将List<Task>传递给Task.WhenAll

此外,如注释中所述,您想使用IQueryable<T>而不是IEnumerable<T>。与数据库的通信必须是异步的,并且在LINQ表达式的 end 中出现。