Marcel
Marcel

Reputation: 15732

NHibernate 3.0: No FirstOrDefault() with QueryOver?

I am playing with FluentNHibernate and NH 3.0, using the LINQ provider and the new QueryOver syntax.

Now with QueryOver I want to get an item (called result) with a timestamp value as close as possible to a given value, but not greater:

 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

Now, Intellisense tells me that there is no such thing as a FirstOrDefault() method. I could, of course, enumerate my ordered query, and then use LINQ to get my item. But this would load all items into memory first.

Is there an alternative to FirstOrDefault(), or have I understood something completely wrong?

Upvotes: 32

Views: 20821

Answers (5)

hplodur
hplodur

Reputation: 1

SetFetchSize(1) is required. If your LINQ query returns more than one result, it will throw an NHibernate exception using UniqueResult(), as it is only expecting one result to be returned from the query.

Upvotes: 0

RRR
RRR

Reputation: 271

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

Upvotes: 23

Diego Mijelshon
Diego Mijelshon

Reputation: 52745

NH 3 has an integrated LINQ provider (queries are translated internally to HQL/SQL). You have to add the NHibernate.Linq namespace and then:

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

Upvotes: 11

Marcel
Marcel

Reputation: 15732

I have now found out that I could use the Take() extension method on the IQueryOver instance, and only the enumerate to a list, like so:

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

Upvotes: 40

tom.dietrich
tom.dietrich

Reputation: 8347

Try

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

UniqueResult will return a single value, or null if no value is found, which is kinda what First or Default does.

Setting the Fetch Size to 1 may or may not be required, I'd test that with a profiler.

Upvotes: 10

Related Questions