NHibernate 3.0:QueryOver中没有FirstOrDefault()方法?

32

我正在尝试使用FluentNHibernate和NH 3.0,使用LINQ提供程序和新的QueryOver语法。

现在,我想使用QueryOver获取一个项(称为result),其时间戳值尽可能接近给定值但不大于它:

 Result precedingOrMatchingResult = Session.QueryOver<Result>().
        Where(r => r.TimeStamp < timeStamp).
        OrderBy(r => r.TimeStamp).Desc.                
        FirstOrDefault(); //get the preceding or matching result, if there is any

现在,Intellisense告诉我FirstOrDefault()方法不存在。当然,我可以枚举我的有序查询,然后使用LINQ获取我的项。但这会首先将所有项加载到内存中。

是否有替代FirstOrDefault()的方法,或者我完全理解错了什么?


请查找 SingleOrDefault()。请接受 @RRR 的答案。 - Brian Chavez
5个回答

40

我现在已经发现可以在IQueryOver实例上使用Take()扩展方法,并且只需将其枚举到列表中,就像这样:

Result precedingOrMatchingResult = Session.QueryOver<Result>().
        Where(r => r.TimeStamp < timeStamp).
        OrderBy(r => r.TimeStamp).Desc.   
        Take(1).List(). //enumerate only on element of the sequence!
        FirstOrDefault(); //get the preceding or matching result, if there is any

6
是的。我本来就想推荐“Take”。再加上RRR的“SingleOrDefault”而不是“List().FirstOrDefault()”,那就完美了。 - Daniel Schilling
实际上,.Take(1).SetFetchSize(1) 的作用是相同的,它们都会告诉 SQL 你只需要一个结果。在这两种情况下,你都不会执行查询以获取所有匹配的结果,然后枚举第一个结果。 - tom.dietrich
只是想提一下评论中有一个错别字。“enumerate only on”应该改为“enumerate only one”。回答得很好。 - Tony

23
Result precedingOrMatchingResult = Session.QueryOver<Result>()
                                          .Where(r => r.TimeStamp < timeStamp)
                                          .OrderBy(r => r.TimeStamp).Desc
                                          .SingleOrDefault();

3
NHibernate.NonUniqueResultException的原因 - Graham
12
在这种情况下,请先使用.Take(1),再使用.SingleOrDefault()。请参考@Marcel的答案。 - Joel Purra
4
这不是正确的答案。如果有多个结果,SingleOrDefault会抛出异常,而FirstOrDefault则不会。但你可以尝试使用.Take(1).SingleOrDefault()。 - wezzix
{btsdaf} - PandaWood
@PandaWood 不是的,OP正在排序并想要顶部结果,因此SingleOrDefault本身不能回答问题。 - AnorZaken

11

NH3拥有一个集成的LINQ提供程序(查询会被内部转换为HQL/SQL)。您需要添加NHibernate.Linq命名空间,然后:

Result precedingOrMatchingResult = Session.Query<Result>().
    Where(r => r.TimeStamp < timeStamp).
    OrderByDescending(r => r.TimeStamp).
    FirstOrDefault();

看起来很不错,我会试一下(不过要等到明年,因为我现在休假了!) - Marcel
这样做很管用。然而,由于我的Where子句中有一些额外的限制,我发现LINQ to NH不支持Nullable类型的.HasValue()方法。太糟糕了,但现在我检查!= null,这也可以工作。 - Marcel
8
这并没有回答如何使用QueryOver<T>来做这件事的问题。答案是有效的,只是不适用于所问的问题。@RRR的答案更正确。 - Jafin

10

尝试

Result precedingOrMatchingResult = Session.QueryOver<Result>().
        Where(r => r.TimeStamp < timeStamp).
        OrderBy(r => r.TimeStamp).Desc.
        SetFetchSize(1).
        UniqueResult();

UniqueResult将返回一个单一的值,如果没有找到值,则返回null,这有点像First或Default的作用。

将Fetch Size设置为1可能需要,也可能不需要,建议使用性能分析工具进行测试。


我已经研究了一下。UniqueResult 看起来类似于SingleOrDefault,但我没有单个项目,我有一个列表,并且我只想选择第一个项目。 - Marcel
你想要查询返回的只是第一项,对吧?为什么要从数据库服务器返回一个列表然后再丢弃大部分的结果呢? - tom.dietrich

0
需要使用 SetFetchSize(1)。如果您的LINQ查询返回多个结果,则会使用UniqueResult()抛出NHibernate异常,因为它只期望从查询中返回一个结果。

1
欢迎来到Stack Overflow并感谢您的贡献!您的回答似乎与问题不完全相关,因为问题中没有提到“SetFetchSize”。也许您可以更清楚地解释或更正。我们的指南如何撰写好的答案可能会给您提供有用的提示。谢谢! - David
嗨,David,我的意思是要评论上面的答案,而不是提供一个全新的答案。我犯了个错误。 - hplodur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接