【问题标题】:Filtering IQueryable sub list过滤 IQueryable 子列表
【发布时间】:2013-05-27 18:48:23
【问题描述】:

使用实体框架,但这可能无关紧要 如果我有一个 IQueryable,我如何过滤子列表并保持它 IQueryable,这样它就不会命中数据库?

如果我有 10 个项目,每个项目有 3 个子项目,我如何过滤它以便返回所有 10 个项目,并且它们的子项目被过滤到 id = 1 的位置?

类 Item 上有大约 20 个属性,因此出于维护问题,我不想对每个属性都使用投影..

items = items.select(??);//how to do this so items are returned, and their children are filtered?

class SubItem
{ private int ID { get; set; }
}
class Item 
{
private List<SubItem> SubItems { get; set; }
}

【问题讨论】:

  • 为什么在发出查询时不包含 where 子句?
  • 好问题。原因是我希望使用假数据对我的过滤器进行单元测试,而不是依赖于会随着时间而改变状态的数据库

标签: c# linq entity-framework filtering


【解决方案1】:

我将您的问题解释为无论如何您都想返回所有Items,但您想过滤SubItems。对于IQueryable,没有好的方法可以说“我想返回这个对象,除非我想要一个修改版的 X 属性”。如果需要,您必须使用 select 语句来选择一个新对象。

选项1:单独返回数据

var itemsAndSubItems = items
    .Select(item => new 
        {
            Item = item,
            SubItems = item.SubItems.Where(sub => sub.ID = 1)
        }
    );

或者如果您不介意急切地将项目加载到内存中:

IEnumerable<Item> = items
    .Select(item => new 
        {
            Item = item,
            SubItems = item.SubItems.Where(sub => sub.ID = 1)
        }
    )
    .ToList()
    .Select(row => 
        { 
            var item = row.Item;
            item.SubItems = row.SubItems;
            return item;
        }
    );

选项 2:返回您的类的新实例(您似乎不想这样做)

IQueryable<Item> items = items
    .Select(item => new Item 
        { 
            SubItems = item.SubItems.Where(sub => sub.ID == 1),
            OtherProp = item.OtherProp
            /*etc for the other properties on Item*/
        }
    );

选项 3:向您的班级添加另一个属性。我至少推荐这个。请注意,当您访问 SubItemsWithIdOne 时,您的查询仍会在此处返回所有子项

class Item 
{
    private List<SubItem> SubItems { get; set; }
    private List<SubItem> SubItemsWithIdOne 
    {
        get 
        {
            return this.SubItems.Where(sub => sub.ID == 1); 
        }
    }
}

选项 4:在 SubItem 上添加一个引用其父级 Item 的属性。然后返回SubItem 的列表。这样,您将同时拥有满足您的条件的 SubItemsItems

...如果您使用的是IEnumerable,您可以这样做:

IEnumerable items = items
    .Select(item =>
        {
            item.SubItems.Where(sub => sub.ID = 1);
            return item;
        }
    );

【讨论】:

    【解决方案2】:

    我认为您正在寻找的是 SelectMany。作为一个例子,你的情况是这样的:

      positiveItems = items.SelectMany(x => x.SubItem).Where(x=> x.ID == 1).Select(x=>x.Item);
      //items still IQueryable , so we can concat it with another IQueryable
    
      negativeItems = items.SelectMany(x=>x.SubItem).Where(x=>x.ID != 1).Select(x=>x.Item);
    
    
      //just an using option
      allItems = positiveItems.Concat(negativeItems);
    

    只是一个建议。对于大量引用对象集,可以使用ValueInjecter,非常简单快捷。我在几个生产项目中使用它,它为我节省了大量时间。

    【讨论】:

      【解决方案3】:
      items.Where(i => i.SubItems.Any(subItem => subItem.Id == 1));
      

      【讨论】:

        【解决方案4】:

        如果要将子项过滤到每个父项只有一个子项,则需要从子项开始,选择其父项,并且不要触摸父项的子项:

        IQueryable<SubItem> childItems = context
            .SubItems.Include("Item")
            .Where(si => si.Id == 1 && si.Item.SomeAttr == someValue);
        //               ^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //                   |                         |
        //                   |           Set a condition on the parent
        //  Set a condition on the child
        

        我假设每个子项都有一个“指向”其父项的链接。

        【讨论】:

          猜你喜欢
          • 2012-11-20
          • 1970-01-01
          • 2013-05-04
          • 1970-01-01
          • 2022-01-08
          • 2013-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多