Filtering IQueryable sub list

前端 未结 4 2051
名媛妹妹
名媛妹妹 2021-01-20 19:33

Working with Entity Framework, but that\'s probably irrelevant If I have an Iqueryable, how do I filter a sub list and keep it IQueryable so it doesn\'t yet hit the DB?

相关标签:
4条回答
  • 2021-01-20 19:46

    I think what you are looking is SelectMany. As an example for your case is something like this:

      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);
    

    And just a suggestion. For high number of reference object set, you can use ValueInjecter It is very simple and fast. I used it couple of production projects and it saved my tons of times.

    0 讨论(0)
  • 2021-01-20 19:51

    I interpret your question as you want to return all Items no matter what, but you want to filter SubItems. There is no good way to say "I want to return this object except I want a modified version of X property" for an IQueryable. You'll have to use a select statement where you select a new object if you want to this.

    Option 1: Return the data separately

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

    or if you don't mind eagerly loading the items into memory:

    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;
            }
        );
    

    Option 2: Return a new instance of your class (which it seems you don't want to do)

    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*/
            }
        );
    

    Option 3: Add another property to your class. I recommend this least. Note that your query will still return all sub items here when you access SubItemsWithIdOne

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

    Option 4: Add a property on SubItem that references it's parent Item. Then return a list of SubItem. This way you'll have both SubItems and Items where your criteria is satisfied.

    ...If you're working with an IEnumerable you can do:

    IEnumerable items = items
        .Select(item =>
            {
                item.SubItems.Where(sub => sub.ID = 1);
                return item;
            }
        );
    
    0 讨论(0)
  • 2021-01-20 19:51

    If you want to filter children down to where there's only one child per parent, you need to start with children, select their parents, and do not touch the parents' subitems:

    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
    

    I assume that each sub-item has a link "pointing" back at its parent.

    0 讨论(0)
  • 2021-01-20 19:52
    items.Where(i => i.SubItems.Any(subItem => subItem.Id == 1));
    
    0 讨论(0)
提交回复
热议问题