我有一个包含网站的简单数据库,每个网站都有一堆帖子。
我正在尝试获取某个站点的所有“公共”帖子(我有一个名为site的变量,它已经是EF提供的一个实例)
第一个显而易见的事情是:
var posts = from post in site.Posts
where post.Public == true
orderby post.PublicationTime descending
select post;
这给我带来了我想要的东西,但是在研究SQL Server Profiler时,WHERE只过滤Public字段,而不是Site。实际上,在SQL Server中运行Profiler捕获的查询确实会带回所有站点中的所有帖子(这显然会在ASP.Net方面稍后进行过滤)。
然后我尝试了:
var posts = from post in db.Posts
where post.Site == site && post.Public == true
orderby post.PublicationTime descending
select post;
同样的结果。
我在这里做了一些根本愚蠢的事情吗? 实体框架是否总是在客户端进行过滤?
谢谢!
丹尼尔
答案 0 :(得分:9)
您需要了解LINQ to Entities和LINQ to Objects之间的区别。在使用实体框架时跟踪这一点非常重要。
当您针对ObjectContext发出查询时,您正在使用LINQ to Entities。这将返回IQueryable。只要您使用的是IQueryable类型的变量,就可以使用LINQ API进一步组合查询,当您最终枚举结果时,它将转换为SQL。
但你说:
(我有一个名为site的变量,它已经是EF带来的一个实例)
在这里,您要查询对象的属性,因此您正在使用LINQ to Objects,而不是LINQ to Entities。这意味着您的查询具有不同的提供程序,并且不会转换为SQL。
关于你的第二个问题:
var posts = from post in db.Posts
where post.Site == site && post.Public == true
orderby post.PublicationTime descending
select post;
EF不允许您对L2E中的实例进行身份比较。你必须比较密钥。尝试:
var posts = from post in db.Posts
where post.Site.Id == site.Id && post.Public
orderby post.PublicationTime descending
select post;
顺便说一句,我将post.Public == true
更改为post.Public
。我认为它更清洁。
答案 1 :(得分:4)
如果有人使用方法语法遇到问题:
如果将Func<TEntity,Boolean>
传递给.Where
方法,则在查询从数据库返回后应用过滤器函数。这是因为.Where
方法的返回值返回IEnumerable。另一方面,如果将Expression<Func<TEntity,Boolean>
传递给.Where
方法,则过滤器函数会生成一个发送到数据库的where子句。这是因为.Where
返回IQueryable。只要您坚持使用IQueryables,您就构建了要发送到数据库的查询。当您返回IEnumerable时,方法之前的所有内容都用于创建查询,并且IEnumerable之后的所有内容都应用于返回的内容。如果您将lambda函数存储在变量中,这才真正重要。如果您将lambda直接传递到.Where
方法,通常不会有问题。希望这会有所帮助!!
IEnumerable Where:https://msdn.microsoft.com/en-us/library/bb549418(v=vs.110).aspx
IQueryable Where:https://msdn.microsoft.com/en-us/library/bb535040(v=vs.110).aspx