Thaoden
Thaoden

Reputation: 3580

Filter on collection inside collection

I have some classes modeling a guest user account and among others the guest's state and the guest's host:

public class GuestUser
{
    public string LoginID {get; set;}

    public List<GuestDetails> GuestDetailsList {get; set;}
}

public class GuestDetails
{
    public GuestGroupUserState State {get; set;}

    public GuestCreator GuestCreator {get; set;}
    public GuestUser GuestUser {get; set;}
}

public class GuestCreator
{
    public string CreatorLoginID {get; set;}

    public List<GuestDetails> GuestDetailsList {get; set;}
}

public enum GuestGroupUserState 
{
    Unknown = 0,
    Active = 1,
    Locked = Active << 1,
    Deleted = Locked << 1
}

I want to find all GuestUsers and the respective GuestDetails for a given GuestCreator gc that are not in Deleted-State.

So far, I tried

var guestUsers = gc.GuestDetailsList
    .Where(gd => gd.State != GuestGroupUserState.Deleted)
    .Select(gd => gd.GuestUser);

but this obviously gives me all the GuestDetails for the users, also the ones in Deleted state.

Next, I tried with

var guestUsers = userRepo.GuestUsers
    .Where(gu => gu.GuestDetails
        .Any(gd => gd.State != GuestGroupUserState.Deleted && gd.GuestCreator.Equals(gc)));

but again this gives me all GuestDetails for these users, including the ones in Deleted-state.

Is there a way to get all GuestUsers that have a GuestDetail in non-deleted state and for these users, get only the non-deleted GuestDetails? If a GuestUser has only deleted details, it shouldn't appear in the list at all. At the moment, I'm foreach()ing through my user lists generated with the code snippets above and removing the mismatched details entries from each guest - not the most efficient way I think.

Upvotes: 0

Views: 622

Answers (2)

Iain Galloway
Iain Galloway

Reputation: 19210

This "isn't possible" in a strict sense. You can't "filter" the child collections; you need to allocate new ones:-

var guestUsers = ...; // Wherever they're coming from
var filteredUsers = guestUsers
        .Where(x => x.GuestDetails.Any(y => y.State != GuestGroupUserState.Deleted))
        .Select(x => new GuestUser()
                {
                  LoginId == x.LoginId,
                  GuestDetails == x.GuestDetails
                                   .Where(y => y.State != GuestGroupUserState.Deleted)
                                   .ToList()
                });

Or you might instead want to add a property to your GuestUser object:-

public List<GuestDetails> ActiveGuestDetailsList
{
  get { return this.GuestDetailsList.Where(y => y.State != GuestGroupUserState.Deleted); }
}

If you're using EF or similar you'll want to perform the filtering on the database and project onto a DTO instead.

Upvotes: 2

Alexandre Severino
Alexandre Severino

Reputation: 1613

With your current model architecture, you can't get rid of GuestUser's deleted GuestDetails on the GuestDetailsList. To achieve your goal, you'll need to change the

public List<GuestDetails> GuestDetailsList {get; set;}

to:

public GuestDetails GuestDetails {get; set;}

on your GuestUser class. With a one-to-one relationship between GuestUser and GuestDetails you won't have such problems and will be able to get the correct information via:

var guestUsers = gc.GuestDetailsList
    .Where(g => g.State != GuestGroupUserState.Deleted)
    .Select(g => g.GuestUser);

Upvotes: 1

Related Questions