Queryover dynamic fetch with joins

半世苍凉 提交于 2019-12-06 12:07:01

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

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.

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

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();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!