I'm trying to create a complex Linq query that goes like this: Get all organisations which have employees that match the given filter parameters.
Example filter:
- Firstname: John
- Name: Smith
My first attempt:
if (!filter.Name.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.Name.ToLower().Contains(filter.Name.ToLower())));
}
if (!filter.Firstname.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.Firstname.ToLower().Contains(filter.Firstname.ToLower())));
}
if (!filter.ContactNumber.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.ContactNumber.contains(filter.ContactNumber)));
}
The problem with this approach is that when there is someone with the firstname John (ex. John Johnson) in organisation A, and someone with the last name Smith (Jenny Smith) in organisation A. The organisation (A) that contains those two persons gets returned. Which it shouldn't. I only want organisations that have people with the firstname "john" AND the lastname "Smith"
I found a working, but dirty and non-scalable approach:
if (!filter.ContactNumber.IsNullOrWhiteSpace())
{
if (!filter.Name.IsNullOrWhiteSpace() && !filter.Firstname.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.ContactNumber.contains(filter.ContactNumber)
&& p.Name.ToLower().Contains(filter.Name.ToLower())
&& p.Firstname.ToLower().Contains(filter.Firstname.ToLower())));
}
else if (!filter.Name.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.ContactNumber.contains(filter.ContactNumber)
&& p.Name.ToLower().Contains(filter.Name.ToLower())));
} else if (!filter.Firstname.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.ContactNumber.contains(filter.ContactNumber)
&& p.Firstname.ToLower().Contains(filter.Firstname.ToLower())));
} else
{
query = query.Where(o => o.Persons.Any(p => p.ContactNumber.contains(filter.ContactNumber));
}
} else if(!filter.Name.IsNullOrWhiteSpace())
{
if (!filter.Firstname.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.Firstname.ToLower().Contains(filter.Firstname.ToLower()) && p.Name.ToLower().Contains(filter.Name.ToLower())));
} else
{
query = query.Where(o => o.Persons.Any(p => p.Name.ToLower().Contains(filter.Name.ToLower())));
}
} else if (!filter.Firstname.IsNullOrWhiteSpace())
{
query = query.Where(o => o.Persons.Any(p => p.Firstname.ToLower().Contains(filter.Firstname.ToLower())));
}
As you can see this not a very clean solution.
I also tried using method calls inside the expression but Linq couldnt translate that. Is there any way I can can make a list of predicate expressions an merge them to one? Or is there a another, better solution?
By the way, since I need a paginated list, it all has to be in one query.
For your information, this is what my filter class looks like. It is just a class send from my front-end with all the fields that need to be filtered.
public class ContactFilter
{
public string Name{ get; set; }
public string Firstname{ get; set; }
public string ContactNummer { get; set; }
}