Filtering On ThenInclude Three Nested Levels down

大憨熊 提交于 2020-05-30 03:49:27

问题


I am trying to filter three child levels down and find only child elements where PropertyMailingAddress.Status== True.

It is still returning values which are False under PropertyMailingAddress.Status

How do I convert filter three levels down and conduct nested filtering with ThenInclude?

Class Structure is nested like this:

  1. Property
  2. PropertyParty
  3. Party
  4. PartyMailingAddress
  5. PropertyMailingAddress <--- Status should equal true (Any grandchild PropertyMailingAddress nodes with Status == False, should be removed from this nested grandchild branch, keep the PropertyMailingAddress nodes which are True)
var result = await propertyRepository.GetAll()
                .Include(pm => pm.PropertyParty).ThenInclude(x => x.Party).ThenInclude(x => x.PartyMailingAddress).ThenInclude(x => x.PropertyMailingAddress)
                .Where (a=> a.PropertyParty.Any(x=> (x.Party.PartyMailingAddress.Any(z => z.PropertyMailingAddress.Any(h => h.Status.HasValue && h.Status.Value && h.Status == true))))

回答1:


People tend to use Include as some kind of Shortcut for a Select. However, it is usually quite a waste of processing power to include all properties, because you won't use several of them or because you already know the value.

Take for instance a School with Students, a straightforward one-to-many relation. Every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key SchoolId refers to.

So if School [10] has 2000 Students, then every Student will have a value for SchoolId equal to 10. If you query School [10] with its Students, you will be transferring this value [10] more than 2000 times. What a waste of processing power!

In entity framework, use Select to query data, and only select the values that you actually plan to use. Only use Include if you plan to update the included data.

Certainly don't use Include as some kind of Shortcut for "Select all properties"!

Back to you question

  • Every Property has zero or more PropertyParties.
  • Every PropertyParty has zero or more Properties.
  • Every Property has zero or more PartyMailingAddresses
  • Every PartyMailingAddress has zero or more PropertyMailingAddresses
  • Every PropertyMailingAddress has a Boolean property Status

You want to query (several properties of) all Properties, that have deep inside at least one PropertyMailingAddress with a true Status value.

Whenever you have a sequence of Items where every Item has a subsequence of OtherItems, and you want to investigate all OtherItems as if it were one sequence, consider using SelectMany:

var propertiesWithTrueStatus = propertyRepository.GetAll()
    .Where(property => property.SelectMany(property => property.PropertyParties)

        // result: one big sequence of all PropertyParties of all properties
        .SelectMany(propertyParty => propertyParty.PartyMailingAddresses)

        // result: one big sequence of all PartyMailingAddresses of all 
        // PropertyParties of all Properties
        .SelectMany(partyMailingAddress => partyMailingAddress.PropertyMailingAddresses)
        .Select(propertyMailingAddress => propertyMailingAddress.Status)

        // result: one big sequence of all Statusses of all PropertyMailingAddresses
        // of all ... of all Properties

        // Keep only the true status values:
        .Where(status => status)

        // keep only the Properties that have at least one true Status value
        .Any())
        // end of Where(...)

So now you have only those Properties that deep inside have at least one true Status value. Continue the query with a Select (or if you really want: Include)

    .Select(property => new
    {
        // Select only the properties that you actually plan to use
        Id = property.Id,
        Name = property.Name,
        ...

        PropertyParties = property.PropertyParties.Select(propertyParty => new
        {
            // again only the properties that you plan to use:
            Id = propertyParty.Id,
            ...

            // no need to Select this, you already know the value
            // PropertyId = propertyParty.PropertyId

            PartyMailingAddresses = propertyParty.PartyMailingAddresses
                .Select( partyMailingAddress => new { ... } )
                .ToList(),
        })
        .ToList(),
    });

Apart from that Select is more efficient than Include, it gives you more freedom to deviate from your database tables if needed. If you don't need all PropertyMailingAddresses in your end result, simply don't select them. If you only want the total number of PropertyParties, use PropertyPartyCount = propertyParties.Count. With Select the returned data does not have to be similar to your database tables.

This has the advantage that you can hide changes in your database: simply change the Select, and all your users won't notice that deep inside you have changed your tables.



来源:https://stackoverflow.com/questions/61861869/filtering-on-theninclude-three-nested-levels-down

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!