使用联接查询动态获取 [英] Queryover dynamic fetch with joins

查看:67
本文介绍了使用联接查询动态获取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用nhibernate尝试新查询并发现新问题:(

iam trying a new query with nhibernate and find a new problem :(

以此为模型:

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集合的对象:

i want A object that have a particular B object and dinamically eager fetch Cs or Ds collection of selected B:

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

但不施加急切的负载:如果我对此进行测试:

but eager load is not applyed: if i test this:

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

我看到执行了许多查询,并且CsDs引发了惰性初始化错误

i see many query executed and Cs and Ds throw lazy inizializazion error

我发现并阅读渴望没有左联接的问题,因此将第一部分切换到此:

i found this and read that eager have problem without leftJoin so switch first part to this:

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作为提取的参数选择

looking at similar fetch done in other case seem that possible cause can be a.Bs.First().Ds as parameter selection for 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)

推荐答案

看着您的实际麻烦,急于加载,我不明白您为什么要这样处理.从逻辑上讲,您当前的代码应该是错误的:它会使用您的过滤条件发出查询,然后执行一堆使用急切加载的属性加载所有A实体"的查询...

Looking at your actual trouble, eager loading, I do not see why you put future in it that way. Your current code should logically be wrong: it issues a query with your filtering criteria then a bunch of "load all A entities with an eager loaded property" queries...

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

If your fetched properties are not collections (or only one is a collection), you should write it that way:

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调用它.

This is a single query, immediately executed after being created: if you do not have other Future query awaiting execution, there is no point calling it with Future.

如果您有很多想要加载的集合,这将导致笛卡尔积.为了避免这种情况,您需要将它们拆分为多个查询. Future可能有用. (但是再次如您的问题注释中所述,延迟加载这样做更合适:无需出于急切的加载考虑而膨胀加载逻辑,只需进行设置映射中的批处理大小,并确保在关闭会话之前完成使用实体的操作.)

If you have many collections to eager load, this would result in a Cartesian product. To avoid it you then need to split them in many queries. There Future could be useful. (But once again as said in your question comments, lazy-loading fare better for this: no need to bloat the loading logic with eager loading considerations, just setup batching size in mappings, and ensure you are done using your entities before closing the session.)

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及更高版本,如果您的数据提供者实际上不支持Future(在单个SQL命令中有多个查询),则未明确执行的Future将被丢弃而不会被执行. (以前的版本会在Future调用中立即执行将来的查询,而数据提供者实际上并不支持它们.)

Please note that with NHibernate 5 and above, if your data provider does not actually support future (multiple queries in a single SQL command), futures which have not been explicitly executed will be discarded without being executed. (Previous versions were executing future queries immediately at the Future call with data providers not actually supporting them.)

为了即使在数据提供者不支持期货的情况下也要执行它们,请将最后一行更改为:

For having them executed even with data provider not supporting futures, change the last line to:

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

这篇关于使用联接查询动态获取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆