我正在添加一个新手答案,因为这些答案似乎在我的脑海中,直到我意识到它是多么简单。有时,您的期望是它很复杂,这让您无法“绕开它”。
直到我遇到一个非常烦人的“错误”,试图通用地使用 LINQ-to-SQL:
public IEnumerable<T> Get(Func<T, bool> conditionLambda){
using(var db = new DbContext()){
return db.Set<T>.Where(conditionLambda);
}
}
在我开始在更大的数据集上遇到 OutofMemoryExceptions 之前,这非常有效。在 lambda 中设置断点让我意识到它正在逐一遍历表中的每一行,以寻找与我的 lambda 条件匹配的内容。这让我困惑了一段时间,因为它为什么将我的数据表视为一个巨大的 IEnumerable 而不是像它应该做的那样做 LINQ-to-SQL?它也在我的 LINQ-to-MongoDb 对应项中做同样的事情。
解决方法只是将Func<T, bool> 转换为Expression<Func<T, bool>>,所以我在谷歌上搜索了为什么它需要Expression 而不是Func,到此结束。
表达式只是将委托转换为关于其自身的数据。因此a => a + 1 变成类似于“在左侧有一个int a。在右侧你添加 1。 " 就是这样。你现在可以回家了。它显然比这更有条理,但这基本上就是一个表达式树的全部内容——没有什么可以让你头疼的。
了解这一点后,为什么 LINQ-to-SQL 需要 Expression 而 Func 是不够的就很清楚了。 Func 并没有提供一种进入自身的方式,以了解如何将其转换为 SQL/MongoDb/其他查询的细节。你看不到它是在做加法还是乘法或减法。你所能做的就是运行它。另一方面,Expression 允许您查看代理内部并查看它想要做的所有事情。这使您能够将委托转换为您想要的任何内容,例如 SQL 查询。 Func 不起作用,因为我的 DbContext 对 lambda 表达式的内容视而不见。因此,它无法将 lambda 表达式转换为 SQL;但是,它做了次好的事情,并在我的表中的每一行中迭代该条件。
编辑:应约翰彼得的要求解释我的最后一句话:
IQueryable 扩展了 IEnumerable,因此像 Where() 这样的 IEnumerable 方法获得了接受 Expression 的重载。当您将Expression 传递给它时,您会保留一个IQueryable,但是当您传递Func 时,您将退回到基本的IEnumerable,结果您将获得一个IEnumerable。换句话说,你没有注意到你已经把你的数据集变成了一个要迭代的列表,而不是要查询的东西。除非您真正深入了解签名,否则很难发现差异。