使用 NHibernate 急切加载 child 属性 和 grandchildren

Eager load child property and grandchildren with NHibernate

我有一个 class 模型图,如下所示

A { ISet<B> Bset; int Id;}
B { ISet<C> Cset; D Dprop; } 
D { int Id; }

所有属性都配置为延迟加载。我正在尝试 编写一个查询来加载所有图 ,从 A 开始。理想情况下,它会像

var result = (from conf in s.Query<A>()
    .FetchMany(x => x.Bset)
    .ThenFetch(x => x.Dprop)
    // line below doesn't work, because x 
    //is a D object here, and not a "B", as I would like
    .ThenFetchMany(x => x.Cset) 
     where conf.Id == 42
     select conf).SingleOrDefault();

因此,当我尝试获取 Cset 关联时,我需要做的是 "go up one level"。有人知道怎么做吗?

我正在使用 Nhibernate 4.1.0。

您必须从 FetchMany 重新开始。

var result = (from conf in s.Query<A>()
    .FetchMany(x => x.Bset)
    .ThenFetch(x => x.Dprop)
    .FetchMany(x => x.Bset)
    .ThenFetchMany(x => x.Cset) 
     where conf.Id == 42
     select conf).SingleOrDefault();

但我担心这会导致笛卡尔积,重复结果 and/or 导致性能不佳。

最好切换到延迟加载。通过 enabling batch loading of lazy loads.

避免 N+1 select 个问题

如果您需要关闭会话然后使用您的实体,您将必须通过在它们的延迟加载属性上调用 NHibernateUtil.Initialize() 来触发延迟加载,在您的实体上循环。由于延迟加载批处理,它不会对已经加载的那些做任何事情。
另一种选择是在关闭会话之前将您的实体转换为类似于视图模型的东西。

Frédréric 回答,一种方法是重复 FetchMany 子句,(从图中的第一个实体开始)再次到达 Cset:

var result = (from conf in s.Query<A>()
.FetchMany(x => x.Bset).ThenFetch(x => x.Dprop)
.FetchMany(x => x.Bset).ThenFetchMany(x => x.Cset) 
 where conf.Id == 42
 select conf).SingleOrDefault();

使用集合进行关联,此解决方案不会在生成的 SQL 和 NHibernate 返回的对象中产生笛卡尔积。

另一种解决方案是使用 Future Query ,它将在触发实际查询时(就在它之前)执行:

var queryAux =  (from conf in s.Query<A>()
    .FetchMany(x => x.Bset)
    .ThenFetch(x => x.Dprop) 
    where conf.Id == 42 select conf).ToFuture();

var result = (from conf in s.Query<A>()
    .FetchMany(x => x.Bset)
    .ThenFetch(x => x.Dprop)    
     where conf.Id == 42
     select conf).SingleOrDefault();

这样,将向数据库触发 2 个查询,但它也有效。