动态过滤Linq Lambda表达式

3
我目前正在尝试做以下事情。
var groups = MileId == null ? test.Groups.Where(x => x.ProjectId == ProjectId)
                            : test.Groups.Where(x => x.Milestone == MileId &&
                                                     x.ProjectId == ProjectId);

但我也有额外的条件需要按组过滤:

foreach (var ChartItem in ChartItems)
{
     foreach (var StatusItem in ChartItem.ChartStatusItems)
     {
          foreach (var PriorityItem in StatusItem.ChartPriorityItems)
          {
              filteredgroups.AddRange(
                  groups.Where(x => x.Status   == StatusItem.StatusID
                                &&  x.Priority == PriorityItem.PriorityID));
          }
     }
}

这样做没有问题,可以正常工作,但嵌套的foreach循环在添加范围时速度相当慢。如果在循环之前使用groups.toList(),那么这个语句就变得很慢了,而嵌套的循环则很快。
我的问题是:
是否有可能根据这些动态的StatusIds和PriorityIds从一开始就过滤掉组?如何实现?
Stackoverflow根据我的主题推荐了一些基于表达式树的文章......那是我需要研究的吗?
谢谢!
编辑:
所以现在我正在做这个:
        foreach (var ChartItem in ChartItems)
        {
            foreach (var StatusItem in ChartItem.ChartStatusItems)
            {
                foreach (var PriorityItem in StatusItem.ChartPriorityItems)
                {

                    var groups = MileId == null ? test.Groups.Where(x => x.ProjectId == InspectorProjectId &&
                                                                                    x.Status == StatusItem.StatusID &&
                                                                                    x.Priority == PriorityItem.PriorityID)
                                                       : test.Groups.Where(x => x.Milestone == InspectorMileId &&
                                                                                    x.ProjectId == InspectorProjectId &&
                                                                                    x.Status == StatusItem.StatusID &&
                                                                                    x.Priority == PriorityItem.PriorityID);

                    filteredgroups.AddRange(groups);
                }
            }
        }

这是一个很大的改进,但每个优先级仍然需要发送到缓慢的“测试”服务器。如果我能一次性过滤掉所有问题,那就太理想了。

编辑2:哦,我没有直接访问数据库的权限:( 我们通过API来访问它。


1
test.Groups是什么?它是在访问数据库吗?这个列表大约包含多少项? - svick
是的,没错,test.groups正在访问数据库。在过滤之前,groups包含大约10k个项目。 - RoboKozo
4个回答

4

所有这些都应该在数据库中发生。只需创建一个连接所有这些表的视图。在交集和数据集的连接方面,很难比数据库更快。


是的,我最终以类似的方式进行了优化(请参见编辑)。你有什么建议? - RoboKozo
很抱歉,创建视图不是一个选项,因为我没有直接访问数据库的权限。相反,我们通过 API 与其通信。 - RoboKozo

1

你能用Contains做到吗?

var filteredgroups =
  test.Groups.Where(x => 
    (MileId == null || x.Milestone == MileId) // (replaces ?: in original)
    && x.ProjectId == ProjectId
    && ChartItem.ChartStatusItems.Contains(x.Status) 
    && StatusItem.ChartPriorityItems.Contains(x.Priority));

(我不确定Linq-to-Sql和Linq-to-Objects在性能方面如何交互,但至少它很简洁...)


我在这里也做了类似的事情,但是我没有使用contains,而是使用了以下代码: x.Status == StatusItem.StatusID && x.Priority == PriorityItem.PriorityID - RoboKozo

0

foreach循环很可能执行延迟调用,这很可能会在每个foreach循环中访问您的数据库。但是您不必这样做,使用SelectMany,您可以简单地构建查询:

var statuses = ChartItems
                   .SelectMany(x => x.ChartStatusItems)
                   .Select(i => i.StatusId);
var priorities = ChartItems
                     .SelectMany(x => x.ChartPriorityItems)
                     .Select(i => i.PriorityId); 

var filtered = groups.Where(x => statuses.Contains(x.Status) &&
                                 priorities.Contains(x.Priority))

0
也许你可以在你的 .Where() 中调用 .Any(),从而完全跳过循环。
test.Groups.Where(x => (MileId == null || 
                                                 x.Milestone == MileId) && 
                                                 x.ProjectId == ProjectId && 
                                                 ChartItems.Any(c => c.ChartStatusItems.Any(s => s.StatusId == x.StatusId && 
                                                     s.ChartPriorityItems.Any(p => p.PriorityId == x.PriorityId)))); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接