Linq Count里面的Select,嵌套,最小化立即执行数据库依赖调用

时间:2016-09-23 09:53:25

标签: c# entity-framework linq azure

我有一个大量的LINQ查询,可以获取如下信息:

enter image description here

换言之,拥有第二级类别的第一级类别拥有第三级类别。对于每个类别,我们检索它包含的列表数量。

以下是查询:

categories = categoryRepository
           .Categories
           .Where(x => x.ParentID == null)
           .Select(x => new CategoryBrowseIndexViewModel
           {
               CategoryID = x.CategoryID, 
               FriendlyName = x.FriendlyName, 
               RoutingName = x.RoutingName, 
               ListingCount = listingRepository
                              .Listings
                              .Where(y => y.SelectedCategoryOneID == x.CategoryID
                                       && y.Lister.Status != Subscription.StatusEnum.Cancelled.ToString())
                              .Count(),

               BrowseCategoriesLevelTwoViewModels = categoryRepository
                    .Categories
                    .Where(a => a.ParentID == x.CategoryID)
                    .Select(a => new BrowseCategoriesLevelTwoViewModel
                    {
                        CategoryID = a.CategoryID,
                        FriendlyName = a.FriendlyName,
                        RoutingName = a.RoutingName,
                        ParentRoutingName = x.RoutingName,
                        ListingCount = listingRepository
                                       .Listings
                                       .Where(n => n.SelectedCategoryTwoID == a.CategoryID
                                                && n.Lister.Status != Subscription.StatusEnum.Cancelled.ToString())
                                       .Count(),

                        BrowseCategoriesLevelThreeViewModels = categoryRepository
                                         .Categories
                                         .Where(b => b.ParentID == a.CategoryID)
                                         .Select(b => new BrowseCategoriesLevelThreeViewModel
                                         {
                                             CategoryID = b.CategoryID,
                                             FriendlyName = b.FriendlyName,
                                             RoutingName = b.RoutingName,
                                             ParentRoutingName = a.RoutingName,
                                             ParentParentID = x.CategoryID,
                                             ParentParentRoutingName = x.RoutingName,
                                             ListingCount = listingRepository
                                                            .Listings
                                                            .Where(n => n.SelectedCategoryThreeID == b.CategoryID
                                                                     && n.Lister.Status != Subscription.StatusEnum.Cancelled.ToString())
                                                            .Count()
                                         })
                                         .Distinct()
                                         .OrderBy(b => b.FriendlyName)
                                         .ToList()
                    })
                    .Distinct()
                    .OrderBy(a => a.FriendlyName)
                    .ToList()
           })
           .Distinct()
           .OrderBy(x => x.FriendlyName == jobVacanciesFriendlyName)
           .ThenBy(x => x.FriendlyName == servicesLabourHireFriendlyName)
           .ThenBy(x => x.FriendlyName == goodsEquipmentFriendlyName)
           .ToList();

这在我的开发机器上足够快,但唉!部署到Azure它的速度非常慢。原因似乎是这个查询正在对数据库进行数百次依赖调用,我非常肯定,因为它立即执行了Count语句。虽然应用程序和数据库位于同一个数据中心,但是这些调用的加入方式与我们的开发机器上的相同(约40秒vs< 1s)。所以我想要做的就是将整个事情发送到数据库,让它紧缩,如果可能的话,将其全部收回。我该怎么做呢?另外,如果我接近这件事,请告诉我。这是我的网络应用程序中最大的瓶颈,所以任何有助于提高效率的帮助都值得赞赏。谢谢! (我对Web应用程序内存使用的关注程度低于我对所有数据库调用的累积影响。)

2 个答案:

答案 0 :(得分:1)

这是我对您的大量查询的建议。

  1. 不要在内部查询中使用ToList()

  2. 不要在内部查询中使用Count()

  3. 尝试检索所有数据once而不进行IEnumerable次操作。换句话说,将数据提取为IQueryable模式。将其加载到App的内存后,您可以创建数据模型如您所愿。这个过程将为您的应用程序带来巨大的性能提升。所以请尝试并让我们知道。

    关于Count()

    更新

    如果该列表中包含大量列,则只需使用Count()获取不含projection的1列。之后,您可以在count()列表中获取IEnumerable 。换句话说,在从db获取应用程序的内存之后。

答案 1 :(得分:1)

这是我到目前为止所得到的。它工作得非常好,但我仍然很好奇我是否可以在一次数据库旅行中做到这一点,而不是两次。由于每个存储库都有自己的DBContext,这似乎很复杂。如果你们有更多的想法,我会非常乐意为你们提供投资。

var allCategories = categoryRepository
                    .Categories
                    .Select(x => new
                    {
                        x.CategoryID,
                        x.FriendlyName,
                        x.RoutingName,
                        x.ParentID
                    })
                    .ToList();

var allListings = listingRepository
                  .Listings
                  .Where(x => x.Lister.Status != Subscription.StatusEnum.Cancelled.ToString())
                  .Select(x => new
                  {
                      x.SelectedCategoryOneID,
                      x.SelectedCategoryTwoID,
                      x.SelectedCategoryThreeID,                                      
                  })
                  .ToList();

            categories = 
            allCategories
           .Where(x => x.ParentID == null)
           .Select(a => new CategoryBrowseIndexViewModel
           {
               CategoryID = a.CategoryID,
               FriendlyName = a.FriendlyName,
               RoutingName = a.RoutingName,
               ListingCount = allListings
                              .Where(x => x.SelectedCategoryOneID == a.CategoryID)
                              .Count(),

               BrowseCategoriesLevelTwoViewModels = 
                    allCategories
                    .Where(x => x.ParentID == a.CategoryID)
                    .Select(b => new BrowseCategoriesLevelTwoViewModel
                    {
                        CategoryID = b.CategoryID,
                        FriendlyName = b.FriendlyName,
                        RoutingName = b.RoutingName,
                        ParentRoutingName = a.RoutingName,
                        ListingCount = allListings
                                       .Where(x => x.SelectedCategoryTwoID == b.CategoryID)
                                       .Count(),

                        BrowseCategoriesLevelThreeViewModels = 
                            allCategories
                            .Where(x => x.ParentID == b.CategoryID)
                            .Select(c => new BrowseCategoriesLevelThreeViewModel
                            {
                                CategoryID = c.CategoryID,
                                FriendlyName = c.FriendlyName,
                                RoutingName = c.RoutingName,
                                ParentRoutingName = b.RoutingName,
                                ParentParentID = a.CategoryID,
                                ParentParentRoutingName = a.RoutingName,
                                ListingCount = allListings
                                               .Where(x => x.SelectedCategoryThreeID == c.CategoryID)
                                               .Count()
                            })
                            .OrderBy(x => x.FriendlyName)
                    })
                    .OrderBy(x => x.FriendlyName)
           })
           .OrderBy(x => x.FriendlyName == jobVacanciesFriendlyName)
           .ThenBy(x => x.FriendlyName == servicesLabourHireFriendlyName)
           .ThenBy(x => x.FriendlyName == goodsEquipmentFriendlyName);