Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq expressions syntax and Compile

I was looking up an example how to use a particular part of the Telerik grid framework (ASP.NET MVC3, but that's not really relevant here). They have a chunk of code which takes a list of filter descriptions and builds an expression:

System.Linq.Expressions.Expression<Func<MyModel, bool> exp = 
               ExpressionBuilder.Expression<MyModel>(listOfFilters);

Ok, so I think that's fine. The Expression wraps a lambda, which operates on MyModel to produce a bool. Great. Now, their example simply drops that into a Where like so:

someList = someList.Where(exp);

Which I am presuming should be "hey apply that expression to all the items in the list (which is of course generic MyModel too). However, VS claims that code doesn't compile. I get "No overload Where exists or System.Func has some invalid arguments".

I played around with it and find that I can compile the expression, which gives the more unseemly looking

someList = someList.Where(x => exp.Compile()(x));

Which does compile and would probably work, but it makes me uncomfortable because I'm now clearly operating outside what I know.

Is there some reason why (IDE setting, flag, stale documentation) the example's approach doesn't work? Is there a rough equivalency to my hack and the example? Should I structure that hack differently to avoid some hideous problem (like, its not going to compile the expression each and every time it checks an item in the list, right? I think it's smart enough for that?)

-- Edit Yes it was IEnumerable. I fell into the "all Wheres are created equal" trap. Thanks all!

like image 476
Mikeb Avatar asked Feb 22 '23 07:02

Mikeb


2 Answers

There are two versions of the Where() method: one operates on IEnumerable<T> and takes Func<T, bool>, the other works on IQueryable<T> and takes Expression<Func<T, bool>>. The reason for that is that IQueryable<T> can represent some remote data, like a database table. And when you query the table, you don't want to transmit the whole table just to find the single item you're looking for.

That's the reason why there is an Expression: it's used to represent some code, but in a way that can be easily processed, and for example converted to SQL. Or you can compile it to normal IL code and make a delegate out of it.

If you want to operate on a normal in-memory list (that implements IEnumerable<T>, but not IQueryable<T>), you don't need expressions at all, you can work with delegates all the time and your code will be probably simpler and faster.

But if you want to work with Expressions, you can, as you discovered. There are several options:

  1. Use the AsQueryable() method. This way, you will get methods like Where() that work with Expressions.

    someList.AsQueryable().Where(exp)
    
  2. Compile the expression by calling the Compile() method. It returns a delegate that you can pass directly to Where().

    someList.Where(exp.Compile())
    

Your attempt actually will compile the expression for each item in the collection, so it will probably have bad performance.

like image 52
svick Avatar answered Feb 23 '23 21:02

svick


Have you tried just doing

someList = someList.Where(exp.Compile());

If someList is an IEnumerable you will need to use exp.Compile() which returns a Func<MyModel,bool>

If someList is an IQueryable you can use exp.Compile() or you can use exp which is an Expression<Func<MyModel, bool>>

like image 42
Aducci Avatar answered Feb 23 '23 21:02

Aducci