Linq To Entities - how to filter on child entities

Yeonho picture Yeonho · Jan 18, 2011 · Viewed 23.4k times · Source

I have entities Group and User.
the Group entity has Users property which is a list of Users.
User has a property named IsEnabled.

I want to write a linq query that returns a list of Groups, which only consists of Users whose IsEnabled is true.

so for example, for data like below
AllGroups
Group A
User 1 (IsEnabled = true)
User 2 (IsEnabled = true)
User 3 (IsEnabled = false)

Group B
User 4 (IsEnabled = true)
User 5 (IsEnabled = false)
User 6 (IsEnabled = false)

I want to get
FilteredGroups
Group A
User 1 (IsEnabled = true)
User 2 (IsEnabled = true)

Group B
User 4 (IsEnabled = true)

I tried the following query, but Visual Studio tells me that
[Property or indexer 'Users' cannot be assigned to -- it is read only]

FilteredGroups = AllGroups.Select(g => new Group()
                    {
                        ID = g.ID,
                        Name = g.Name,
                        ...
                        Users = g.Users.Where(u => u.IsInactive == false)
                    });

thank you for your help!

Answer

Yakimych picture Yakimych · Jan 18, 2011

There is no "nice" way of doing this, but you could try this - project both, Group and filtered Users onto an anonymous object, and then Select just the Groups:

var resultObjectList = AllGroups.
                       Select(g => new
                               {
                                   GroupItem = g,
                                   UserItems = g.Users.Where(u => !u.IsInactive)
                               }).ToList();

FilteredGroups = resultObjectList.Select(i => i.GroupItem).ToList();

This isn't a documented feature and has to do with the way EF constructs SQL queries - in this case it should filter out the child collection, so your FilteredGroups list will only contain active users.

If this works, you can try merging the code:

FilteredGroups = AllGroups.
                 Select(g => new
                               {
                                   GroupItem = g,
                                   UserItems = g.Users.Where(u => !u.IsInactive)
                               }).
                 Select(r => r.GroupItem).
                 ToList();

(This is untested and the outcome depends on how EF will process the second Select, so it would be nice if you let us know which method works after you've tried it).