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

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

问题描述

我正在尝试使用 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

我发现 this 并阅读如果没有leftJoin,急切有问题,所以将第一部分切换到这个:

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

但有同样的问题

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

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 及更高版本中,如果您的数据提供者实际上不支持未来(单个 SQL 命令中的多个查询),则未明确执行的未来将被丢弃而不被执行.(以前的版本在 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天全站免登陆