NHibernate IQueryable 似乎没有延迟执行
我正在使用 NHibernate 3.2,并且我有一个如下所示的存储库方法:
public IEnumerable<MyModel> GetActiveMyModel()
{
return from m in Session.Query<MyModel>()
where m.Active == true
select m;
}
其按预期工作。然而,有时当我使用此方法时,我想进一步过滤它:
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.ID < 100
select new { m.Name };
它会产生与第一个相同的 SQL,而第二个过滤器和选择必须在事后完成。我认为 LINQ 的全部意义在于,它形成了一个表达式树,在需要时将其展开,因此可以为该作业创建正确的 SQL,从而节省我的数据库请求。
如果不是,这意味着我的所有存储库方法都必须准确返回所需的内容,并且我无法在不承受惩罚的情况下进一步使用 LINQ。
我有错吗?
已更新
响应以下评论:我省略了迭代结果的行,这导致运行初始 SQL (WHERE Active = 1) 和第二个过滤器 (ID < 100)显然是在.NET 中完成的。
另外,如果我用它替换第二块代码
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.Items.Count > 0
select new { m.Name };
,它会生成初始 SQL 来检索活动记录,然后为每个记录运行单独的 SQL 语句以找出它有多少个 Items,而不是编写像我期望的那样的内容:
SELECT Name
FROM MyModel m
WHERE Active = 1
AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0
I'm using NHibernate 3.2 and I have a repository method that looks like:
public IEnumerable<MyModel> GetActiveMyModel()
{
return from m in Session.Query<MyModel>()
where m.Active == true
select m;
}
Which works as expected. However, sometimes when I use this method I want to filter it further:
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.ID < 100
select new { m.Name };
Which produces the same SQL as the first one and the second filter and select must be done after the fact. I thought the whole point in LINQ is that it formed an expression tree that was unravelled when it's needed and therefore the correct SQL for the job could be created, saving my database requests.
If not, it means all of my repository methods have to return exactly what is needed and I can't make use of LINQ further down the chain without taking a penalty.
Have I got this wrong?
Updated
In response to the comment below: I omitted the line where I iterate over the results, which causes the initial SQL to be run (WHERE Active = 1) and the second filter (ID < 100) is obviously done in .NET.
Also, If I replace the second chunk of code with
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.Items.Count > 0
select new { m.Name };
It generates the initial SQL to retrieve the active records and then runs a separate SQL statement for each record to find out how many Items it has, rather than writing something like I'd expect:
SELECT Name
FROM MyModel m
WHERE Active = 1
AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您将从该方法返回
IEnumerable
,这将导致从该点开始进行内存中计算,即使底层序列是IQueryable
。如果您希望允许
GetActiveMyModel
之后的代码添加到 SQL 查询,请改为返回IQueryable
。You are returning
IEnumerable<MyModel>
from the method, which will cause in-memory evaluation from that point on, even if the underlying sequence isIQueryable<MyModel>
.If you want to allow code after
GetActiveMyModel
to add to the SQL query, returnIQueryable<MyModel>
instead.您正在运行 IEnumerable 的扩展方法“Where”,而不是 IQueryable 的扩展方法。它仍然会延迟计算并给出相同的输出,但是它会在输入时计算 IQueryable 并且您在内存中过滤集合而不是针对数据库。
当您稍后在另一个表(计数)上添加额外条件时,它必须从数据库中延迟获取每个 Items 集合,因为它在知道条件之前已经评估了 IQueryable。
(是的,我也想成为 IEnumerable 上的扩展方法来代替虚拟成员,但是,唉,他们不是)
You're running IEnumerable's extension method "Where" instead of IQueryable's. It will still evaluate lazily and give the same output, however it evaluates the IQueryable on entry and you're filtering the collection in memory instead of against the database.
When you later add an extra condition on another table (the count), it has to lazily fetch each and every one of the Items collections from the database since it has already evaluated the IQueryable before it knew about the condition.
(Yes, I would also like to be the extensive extension methods on IEnumerable to instead be virtual members, but, alas, they're not)