我正在尝试使用nhibernate的新查询并找到一个新问题:(
以此为模型:
public class D { int id; }
public class C { int id; }
public class B {
int id;
ICollection<C> Cs;
ICollection<D> Ds;
}
public class A {
int id;
ICollection<B> Bs;
}
我想要一个具有特定B对象的对象,并且非常渴望获取所选B的Cs或Ds集合:
public virtual A Read(int idB, params Expression<Func<Attivita, object>>[] eagerFields)
我从
开始IEnumerable<A> query = _session.QueryOver<A>()
.JoinQueryOver(a => a.Bs)
.Where(b => b.Id == idB)
.Future<A>();
foreach (Expression<Func<A>, object>> field in eagerFields)
_session.QueryOver<A>()
.Fetch(field).Eager
.Future<A>();
return query.First();
但是没有应用急切的负载:如果我测试了这个:
Read(12, a => a.Bs, a.Bs.First().Cs, a.Bs.First().Ds)
我看到执行了很多查询而Cs
和Ds
抛出了懒惰的错误判断错误
我发现this并且在没有leftJoin的情况下读到了急切的问题所以请将第一部分切换到此:
B BB= null;
IEnumerable<A> query =_session.QueryOver<A>()
.Fetch(a => a.Bs).Eager
.Left.JoinAlias(a => a.Bs, () => BB)
.Where(() => BB.Id == idB)
.Future<A>();
但有同样的问题
查看在其他情况下完成的类似提取似乎可能导致a.Bs.First().Ds
作为fetch的参数选择
编辑: 只是为了澄清:
这有效:
IEnumerable<A> query = _session.QueryOver<A>()
.Left.JoinAlias(a => a.Bs, () => BB)
.Where(() => BB.Id == IdB)
.Fetch(a => a.Bs).Eager
.Fetch(a => a.Bs.First().Cs).Eager
.Future<A>();
return query.First();
虽然没有:
IEnumerable<A> query = _session.QueryOver<A>()
.JoinQueryOver(a => a.Bs)
.Where(b => b.Id == idB)
.Future<A>();
foreach (Expression<Func<A>, object>> field in eagerFields)
_session.QueryOver<A>()
.Fetch(field).Eager
.Future<A>();
return query.First();
以这种方式调用:Read(12, a => a.Bs, a.Bs.First().Cs, a.Bs.First().Ds)
答案 0 :(得分:1)
看着你的实际问题,急切的加载,我不明白为什么你把这种方式放在其中。您当前的代码在逻辑上应该是错误的:它会使用您的过滤条件发出一个查询,然后一堆“加载所有具有急切加载属性的实体”查询......
如果您获取的属性不是集合(或者只有一个是集合),那么您应该这样写:
IQueryOver<A, A> query = _session.QueryOver<A>()
.JoinQueryOver(a => a.Bs)
.Where(b => b.Id == idB);
foreach (Expression<Func<A>, object>> field in eagerFields)
query = query
.Fetch(field).Eager;
return query.List().First();
这是一个单独的查询,在创建后立即执行:如果您没有等待执行的其他Future
查询,则无法用Future
调用它。
如果您有很多集合需要加载,这将产生笛卡尔积。为了避免它,您需要在许多查询中拆分它们。 Future
可能有用。 (但是再一次如你的问题评论中所述,延迟加载fare better for this:不需要用加载的考虑因素来填充加载逻辑,只需在映射中设置批处理大小,并确保在关闭之前完成使用实体会话。)
var queryBase = _session.QueryOver<A>()
.JoinQueryOver(a => a.Bs)
.Where(b => b.Id == idB);
var queries = new List<IEnumerable<A>>();
foreach (Expression<Func<A>, 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<A> 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();