使用连接查询动态提取

我正在尝试使用nhibernate的新查询并找到一个新问题:(

以此为模型:

public class D { int id; } public class C { int id; } public class B { int id; ICollection Cs; ICollection Ds; } public class A { int id; ICollection Bs; } 

我想要一个具有特定B对象的对象,并且非常渴望获取所选B的Cs或Ds集合:

 public virtual A Read(int idB, params Expression<Func>[] eagerFields) 

我开始

 IEnumerable query = _session.QueryOver() .JoinQueryOver(a => a.Bs) .Where(b => b.Id == idB) .Future(); foreach (Expression<Func, object>> field in eagerFields) _session.QueryOver() .Fetch(field).Eager .Future(); return query.First(); 

但是没有应用急切的负载:如果我测试这个:

 Read(12, a => a.Bs, a.Bs.First().Cs, a.Bs.First().Ds) 

我看到很多查询被执行, CsDs抛出懒惰的inizializazion错误

我找到了这个并且读到了没有leftJoin的渴望有问题所以切换到第一部分:

 B BB= null; IEnumerable query =_session.QueryOver() .Fetch(a => a.Bs).Eager .Left.JoinAlias(a => a.Bs, () => BB) .Where(() => BB.Id == idB) .Future(); 

但有同样的问题

看看在其他情况下完成的类似提取似乎可能的原因可能是a.Bs.First().Ds作为fetch的参数选择

编辑:只是为了澄清:

这工作:

 IEnumerable query = _session.QueryOver() .Left.JoinAlias(a => a.Bs, () => BB) .Where(() => BB.Id == IdB) .Fetch(a => a.Bs).Eager .Fetch(a => a.Bs.First().Cs).Eager .Future(); return query.First(); 

而这个没有:

 IEnumerable query = _session.QueryOver() .JoinQueryOver(a => a.Bs) .Where(b => b.Id == idB) .Future(); foreach (Expression<Func, object>> field in eagerFields) _session.QueryOver() .Fetch(field).Eager .Future(); return query.First(); 

以这种方式调用: Read(12, a => a.Bs, a.Bs.First().Cs, a.Bs.First().Ds)

看着你的实际问题,急切的加载,我不明白为什么你把这种方式放在它的前面。 您当前的代码在逻辑上应该是错误的:它会使用您的过滤条件发出一个查询,然后一堆“加载所有具有急切加载属性的实体”查询…

如果您获取的属性不是集合(或者只有一个是集合),那么您应该这样写:

 IQueryOver query = _session.QueryOver() .JoinQueryOver(a => a.Bs) .Where(b => b.Id == idB); foreach (Expression, object>> field in eagerFields) query = query .Fetch(field).Eager; return query.List().First(); 

这是一个单独的查询,在创建后立即执行:如果您没有等待执行的其他Future查询,则无法使用Future调用它。

如果您有许多集合需要加载,这将产生笛卡尔积。 为了避免它,您需要在许多查询中拆分它们。 Future可能有用。 (但是再次如你的问题评论中所述,延迟加载对此更好 :不需要用急切的加载考虑来填充加载逻辑,只需在映射中设置批处理大小,并确保在关闭会话之前完成使用您的实体。)

 var queryBase = _session.QueryOver() .JoinQueryOver(a => a.Bs) .Where(b => b.Id == idB); var queries = new List>(); foreach (Expression, object>> field in eagerFields) queries.Add(queryBase .Fetch(field).Eager .Future()); return queries.Count == 0 ? queryBase.List().First() : queries[0].First(); 

请注意,对于NHibernate 5及更高版本,如果您的数据提供者实际上不支持将来(单个SQL命令中的多个查询),则未被执行的未来将被丢弃。 (以前的版本在Future调用中立即执行将来的查询,数据提供者实际上并不支持它们。)

为了让它们在数据提供者不支持期货时执行,请将最后一行更改为:

 if (queries.Count == 0) return queryBase.List().First(); List result; foreach (var q in queries) { // Using the IFutureEnumerable directly as an IEnumerable is deprecated. result = q.GetEnumerable() // Due to a bug, GetEnumerable is not yet enough to trigger execution. .ToList(); } return result.First();