user3754124
user3754124

Reputation: 249

C# predicate list passed to Linq Where clause

I have a long Linq Where clause that I would like to populate with a predicate list.

List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();

filters.Add(p => p.Title != null && p.Title.ToLower().Contains(searchString));
filters.Add(p => p.Notes != null && p.Notes.ToLower().Contains(searchString));
filters.Add(GlobalSearchUser((List < User > users = new List<User>() { p.user1, p.user2, p.user3, p.user4 }), searchString));

notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
      .Where(filters.ToArray()).Take(10).ToList();

However I'm getting this error:

cannot convert from 'System.Linq.Expressions.Expression<System.Func<project.Contracts.DTOs.Note,bool>>[]' to 'System.Func<project.Contracts.DTOs.Note,bool>'

Which is an error on the .where clause. Pulling out the .where compiles just fine.

Upvotes: 9

Views: 13080

Answers (4)

PiotrWolkowski
PiotrWolkowski

Reputation: 8782

I think great answer from Hogan can be simplified and shorten a bit by use of Any and All Linq methods.

To get items that fulfill all the conditions:

var resultAll = listOfItems.Where(p => filters.All(f => f(p)));

And to get the items that fulfill any condition:

var resultAny = listOfItems.Where(p => filters.Any(f => f(p)));

Upvotes: 12

xanatos
xanatos

Reputation: 111850

There are at least two errors in your code:

List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();

change it to

List<Func<Note, bool>> filters = new List<Func<Note, bool>>();

You don't need Expression trees here. You are using IEnumerable<>, not IQueryable<>

notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
  .Where(filters.ToArray()).Take(10).ToList();

There .Where() accepts a single predicate at a time. You could:

notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
  .Where(x => filters.All(x)).Take(10).ToList();

or various other solutions, like:

var notesEnu = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
              .AsEnumerable();

foreach (var filter in filters)
{
    notesEmu = notesEmu.Where(filter);
}

notes = notesEnu.Take(10).ToList();

Because all the .Where() conditions are implicitly in &&.

Upvotes: 8

Hogan
Hogan

Reputation: 70523

You have to loop over your filters and run a test on each one.

You can do it with linq like this to return true if any of your filters are true:

.Where(p => { foreach(f in filters) if (f(p) == true) return(true); return(false)}) 

or like this to to return true if all of your filters are true:

.Where(p => { foreach(f in filters) if (f(p) == false) return(false); return(true)}) 

Upvotes: 4

moarboilerplate
moarboilerplate

Reputation: 1643

You can't just pass an array of predicates to the where method. You need to either iterate over the array and keep calling Where() for each expression in the array, or find a way to merge them all together into one expression and use that. You'll want to use LinqKit if you go the second route.

Upvotes: 0

Related Questions