Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate Linq provider and take() skip() with eager fetching

I've got this simple linq query in NHibernate 3.2 and SQLite provider:

var all = (from book in Session.Query<Book>() select book)
    .Skip(15)
    .Take(15)                
    .ToList();

This query returns correctly 15 entities, but when I try to eager load a dependent collection with FetchMany like this:

var all = (from book in Session.Query<Book>() select book)
    .FetchMany(books => books.Authors)
    .Skip(15)
    .Take(15)                
    .ToList();

I get only 11 entities returned. Is this a bug or something I'm missing?

Here is the resulting SQL query

select
     book0_.Id as Id2_0_,
     author2_.Id as Id0_1_,
     book0_.Title as Title2_0_,
     book0_.Sort as Sort2_0_,
     book0_.TimeStamp as TimeStamp2_0_,
     book0_.PubDate as PubDate2_0_,
     book0_.Series_Index as Series6_2_0_,
     book0_.Author_Sort as Author7_2_0_,
     book0_.Isbn as Isbn2_0_,
     book0_.Lccn as Lccn2_0_,
     book0_.Path as Path2_0_,
     book0_.Flags as Flags2_0_,
     book0_.Uuid as Uuid2_0_,
     book0_.Has_Cover as Has13_2_0_,
     book0_.Last_Modified as Last14_2_0_,
     author2_.Name as Name0_1_,
     author2_.Sort as Sort0_1_,
     author2_.Link as Link0_1_,
     authors1_.book as book0__,
     authors1_.author as author0__
from 
    books book0_ 
  left outer join 
    books_authors_link authors1_ on book0_.Id=authors1_.book left outer join authors author2_ 
  on authors1_.author=author2_.Id
order by book0_.Id asc
limit 15 /* @p0 */ offset 0 /* @p1 */

This effectively limits the result set to 15 rows and not 15 entities as I intended to.

like image 209
zszep Avatar asked Mar 15 '12 22:03

zszep


1 Answers

skip and take are translated to the equivalents in sql which limit on rows and since you are eager fetching with joins there isn't much you can do about it.

to eager fetch the first 15 books you need to:

var all = Session.Query<Book>()
    .Skip(15)
    .Take(15)                
    .ToList();

var ids = all.Select(b => b.Id).ToList();

// fetch all Authors of the books, now the books in all have initialized Authors
Session.Query<Book>()
    .Where(b => ids.Contains(b.Id))
    .FetchMany(books => books.Authors)
    .List();

this has 2 roundtrips though

Update: one roundtrip with QueryOver, maybe you can translate into Linq

var subquery = QueryOver.Of<Book>()
    .Skip(15)
    .Take(15)
    .Select(b => b.Id);

var all = Session.QueryOver<Book>()
    .WithSubquery.WhereProperty(b => b.Id).In(subquery)
    .Fetch(books => books.Authors).Eager
    .ToList();
like image 155
Firo Avatar answered Sep 30 '22 07:09

Firo