I understand lambdas and the Func
and Action
delegates. But expressions stump me. In what circumstances would you use an Expression<Func<T>>
rather than a plain old Func<T>
?
相关问题
- Sorting 3 numbers without branching [closed]
- Graphics.DrawImage() - Throws out of memory except
- Why am I getting UnauthorizedAccessException on th
- 求获取指定qq 资料的方法
- How to know full paths to DLL's from .csproj f
An extremely important consideration in the choice of Expression vs Func is that IQueryable providers like LINQ to Entities can 'digest' what you pass in an Expression, but will ignore what you pass in a Func. I have two blog posts on the subject:
More on Expression vs Func with Entity Framework and Falling in Love with LINQ - Part 7: Expressions and Funcs (the last section)
I'm adding an answer-for-noobs because these answers seemed over my head, until I realized how simple it is. Sometimes it's your expectation that it's complicated that makes you unable to 'wrap your head around it'.
I didn't need to understand the difference until I walked into a really annoying 'bug' trying to use LINQ-to-SQL generically:
This worked great until I started getting OutofMemoryExceptions on larger datasets. Setting breakpoints inside the lambda made me realize that it was iterating through each row in my table one-by-one looking for matches to my lambda condition. This stumped me for a while, because why the heck is it treating my data table as a giant IEnumerable instead of doing LINQ-to-SQL like it's supposed to? It was also doing the exact same thing in my LINQ-to-MongoDb counterpart.
The fix was simply to turn
Func<T, bool>
intoExpression<Func<T, bool>>
, so I googled why it needs anExpression
instead ofFunc
, ending up here.An expression simply turns a delegate into a data about itself. So
a => a + 1
becomes something like "On the left side there's anint a
. On the right side you add 1 to it." That's it. You can go home now. It's obviously more structured than that, but that's essentially all an expression tree really is--nothing to wrap your head around.Understanding that, it becomes clear why LINQ-to-SQL needs an
Expression
, and aFunc
isn't adequate.Func
doesn't carry with it a way to get into itself, to see the nitty-gritty of how to translate it into a SQL/MongoDb/other query. You can't see whether it's doing addition or multiplication on subtraction. All you can do is run it.Expression
, on the other hand, allows you to look inside the delegate and see everything it's wanting to do, empowering you to translate it into whatever you want, like a SQL query.Func
didn't work because my DbContext was blind to what was actually in the lambda expression to turn it into SQL, so it did the next best thing and iterated that conditional through each row in my table.Edit: expounding on my last sentence at John Peter's request:
IQueryable extends IEnumerable, so IEnumerable's methods like
Where()
obtain overloads that acceptExpression
. When you pass anExpression
to that, you keep an IQueryable as a result, but when you pass aFunc
, you're falling back on the base IEnumerable and you'll get an IEnumerable as a result. In other words, without noticing you've turned your dataset into a list to be iterated as opposed to something to query. It's hard to notice a difference until you really look under the hood at the signatures.I don't see any answers yet that mention performance. Passing
Func<>
s intoWhere()
orCount()
is bad. Real bad. If you use aFunc<>
then it calls theIEnumerable
LINQ stuff instead ofIQueryable
, which means that whole tables get pulled in and then filtered.Expression<Func<>>
is significantly faster, especially if you are querying a database that lives another server.