1

There's sooo much literature about fetching and eager loading when doing the actual query using .Fetch

But, once I have a loaded entity - with an empty collection (because I chose not to eager load at query time due to the cartesian product side-effect), can I choose to load a collection a bit later on, say after I've done some paging and I have a concrete List of items?

something like:

var list = (some linq over Session.Query<Entity>)
.Take(10).Skip(2)
.Fetch(x => x.MyCollection)
.ToList();

Session.Fetch<Entity>(list, l => l.OtherCollection);

Edit The point is - i'm already fetching 2 child collections in the Query - makes the query and result set quite sizeable already (see nhibernate Cartesian product). I'd like page the results, get a list of 10 then optionally go back to the database to populate child collection properties of the paged (10, say) result. This is a performance consideration.

2 Answers 2

1

Issue this query

/*we dont need the result*/Session.QueryOver<Entity>()
    .Where(x => x.Id.IsIn(list.Select(l => l.Id)))
    .Fetch(l => l.OtherCollection)
    .ToList();

then nhibernate should initialize the collections on the Entities

EDIT:

to improve initial loading time see http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate

then you can do for exmaple

var results = (some linq over Session.Query<Entity>)
    .Take(10).Skip(2)
    .ToList();

var q = Session.QueryOver<Entity>()
    .Where(x => x.Id.IsIn(list.Select(l => l.Id)))
    .Fetch(l => l.MyCollection)
    .ToFuture();

Session.QueryOver<Entity>()
    .Where(x => x.Id.IsIn(list.Select(l => l.Id)))
    .Fetch(l => l.OtherCollection)
    .ToFuture();

Session.QueryOver<Entity>()
    .Where(x => x.Id.IsIn(list.Select(l => l.Id)))
    .Fetch(l => l.ThirdCollection)
    .ToFuture();

return q.ToList()
Sign up to request clarification or add additional context in comments.

4 Comments

isn't that simply eager loading? im talking about fetching a collection on say a disconnected entity or group of disconnected entities.
@BobTodd didn't know that you have disconnected entities. But it should be trivial to reconnect them with session.Lock(obj, LockMode.None); and then perform the loading. The sessioncache will make sure that the existing entities will be initialized
looks like we've found the same thing (see comment) on my answer - what if the original query is expensive and you only want to execute it once? Thinking of the actual sql to do it, its not going to be pretty as the query would have to look like select * from mycollection where parentid in(id1, id2, id3, id4), but for 10 paged results it far better performance than running the main query over and over
Can you see my latest post on my answer?
0

I've just gone off to read about futures and projections in NHibernate which look promising as a solution...will post more when I find out about it.

This is a solution: http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate, but I still do not like it, as my query itself is quite expensive (uses '%like%@ + paging), so executing 3 or 4 times just to load collections seems expensive

Edit

This is what I have. Lookign at the sql generated, the correct sql is being run and returning expected results, but the collections on the returned results are null. Can you see what's missing?:

public List<Company> CompaniesForLoggedInUser(int pageSize, int pageNumber)
    {
      var list =
        QueryForCompaniesFor(SecurityHelper.LoggedInUsername)
          .Page(pageNumber, pageSize)
          .ToList()
          .FetchCompanyCollections(Session);
      return list;
    }

internal static class CompanyListExtensions
  {
    internal static List<Company> FetchCompanyCollections(this List<Company> companies, ISession session)
    {
      var ids = companies.Select(l => l.Id).ToArray();

      session.QueryOver<Company>()
        .Where(x => x.Id.IsIn(ids))
        .Fetch(l => l.Properties).Eager()
        .Future();

      return session.QueryOver<Company>()
        .Where(x => x.Id.IsIn(ids))
        .Fetch(l => l.UserAccessList).Eager()
        .Future()
        .ToList();
    }
  }

2 Comments

looks ok to me, but can you try session.QueryOver<Company>() .Where(x => x.Id.IsIn(ids)) .Fetch(l => l.UserAccessList).Eager() .Future() .ToList(); return companies;
my prob was ToListing the second future not the first, your original answer was spot on (but missing the ToList)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.