【问题标题】:NHibernate Linq provider and take() skip() with eager fetchingNHibernate Linq 提供程序和 take() skip() 急切获取
【发布时间】:2012-03-15 22:40:55
【问题描述】:

我在 NHibernate 3.2 和 SQLite 提供程序中有这个简单的 linq 查询:

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

此查询正确返回 15 个实体,但是当我尝试像这样使用 FetchMany 急切加载依赖集合时:

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

我只返回了 11 个实体。这是一个错误还是我遗漏了什么?

这是生成的 SQL 查询

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 */

这有效地将结果集限制为 15 行,而不是我想要的 15 个实体。

【问题讨论】:

  • 听起来像 NHibernate 正在生成内连接,而它应该生成左连接。我会多看一下属性 Authors 的映射。
  • 我想,这不是问题,因为我每本书至少有一个作者。在每本书的 foreach 循环中获取作者时,我得到了正确的结果。
  • 有趣的是,我确实返回了 15 行,但只有 11 个实体。两本书有两位作者,一本书有三位作者。我是否更正了 skip 和 take 应该限制返回实体的数量而不是行数?
  • 这种行为确实看起来有点奇怪。人们会从 Linq 查询中假设该限制应该适用于书籍,但该查询限制了组合的数量,从而导致书籍数量减少。我能看到的唯一解决方法是将 AsEnumerable() 放在 FetchMany() 之后和 Skip() 之前,但很难估计这会对性能产生什么影响。
  • 谢谢约阿希姆。这确实有效,但它会加载所有书籍并将结果过滤到内存中。这是我打算通过首先引入分页来防止的。我不知道这种奇怪的行为是否是由于 nHibernate Sqlite 驱动程序或其他问题引起的。我会尝试另一个数据库并发布结果。

标签: linq nhibernate


【解决方案1】:

skip 和 take 被转换为 sql 中的等价物,它限制了行,并且由于您渴望通过连接进行获取,因此您无能为力。

要急切地获取前 15 本书,您需要:

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();

虽然有 2 次往返

更新:QueryOver 往返一次,或许你可以翻译成 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();

【讨论】:

  • 经过深思熟虑,我得出了同样的结论,虽然很遗憾。当指定 take(15) 并将 Query 设置为通用类型 Book 时,人们会期望得到 15 本书。我试图构建一个可以执行此操作的 sql 语句,但想不出解决方案。那么这将是两个查询。
【解决方案2】:
var data = session.QueryOver<EmployeeDetails>()
                                  .Where(x => x.Salary > 2000)
                                  //.Take(2)
                                  .Skip(2)
                                  .Take(2)
                                  .List();
                //if take is before skip then it will skip the last (mentioned digits) rows
                //if take is after skip then it will skip the first (mentioned digits) rows

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    相关资源
    最近更新 更多