1

Imagina i have pretty complex model graph such as for an example:

Orchestra -> Musicians -> Instruments -> Properties
                                      -> Items
                       -> Songs -> Parts

I know in theory how Futures works but how do i load this complete graph lets say for a concrete musician (specified by id).

I know that for each collection on same level i have to create simple one future query to avoid Cartesian product in same query. so when i execute code like this:

using(var session = GetSession()){
    var instrumentQuery = session.QueryOver<Musicians>()
         .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
         .JoinQueryOver<Instruments>(x=>x.Instruments)                     
         .Future();
    var instrumentProperties = = session.QueryOver<Musicians>()
         .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
         .JoinQueryOver<Instrument>(x=>x.Instruments, ()=> instrumentAlias)
         .JoinQueryOver<Property>(()=>instrumentAlias.Properties)
         .Future();
     var instrumentItems = = session.QueryOver<Musicians>()
         .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
         .JoinQueryOver<Instrument>(x=>x.Instruments, ()=> instrumentAlias)
         .JoinQueryOver<Item>(()=>instrumentAlias.Items)
         .Future();
    ...
    ...CONTINUE same future queries for each unique collection       
    ...

    ...
    var result = session.QueryOver<Musician>()
         .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
         .SingleOrDefault<Musician>(); //This query is not future so it should???? load all futures ???

    return result;

}

But even when the last query is NOT future it wont send those FUTUR..ISH queries to DB ( i checked with SQL profiler... there is no such SQL heading to DB)

musician.instrument still throws lazy initialization exception.

This code is only demostrative and purely theoretical.

What i want to avoid is:

  • using HQL ( i hate those magic strings...)
  • Change mappings to load eagerly everything because there are use cases i need only musician and intrument ? (or any other subset)
  • avoid N+1 queries problem here
  • leave session open for too long because these data could change from other server instance

What i want to achieve

  • either force nhibernate to correstly create and construct object structure from provided criteria.

or

  • Force nhibernate to use fetch select for this query only

there is also simmilar question: How to load a large, complex object graph using NHibernate

and the answer is... Change your mappings... which i dont want because i dont see a point to load this complex graph for each use cast (even the simple ones)

Technical background:

  • we use Nhibernate 4.0
  • as a DB probably Azure DB or SQL Server will be used (or maybe postgreSQL)
Community
  • 1
  • 1
LightCZ
  • 695
  • 1
  • 8
  • 20

1 Answers1

2

I would say, that the way here would be

  • to use Fetch instead of JoinQuery and
  • place final result into Future as well

So this would be the updated snippet:

var instrumentQuery = session.QueryOver<Musicians>()
     .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
     .Fetch(x=>x.Instruments).Eager
     .Future();
var instrumentProperties = = session.QueryOver<Musicians>()
     .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
     .Fetch(x=>x.SecondCollection).Eager
     .Future();

...
...CONTINUE same future queries for each unique collection       
...

...
var result = session.QueryOver<Musician>()
     .Where(x=>x.Id == CONCRETEMUSICIANIDTHERE)
     // all will be fetaures
     .Future()
     .SingleOrDefault<Musician>();

NOTE: I would go different way. Load just root object (Musician). Use batch-size for optimized fetching. Create DTO while session is open.

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Are you sure about this? ......Fetch(x=>x.Instruments, ()=>InstrumentAlias).Eager.Fetch(()=>instrumentAlias.Properties).Eager.....Wouldnt this create cartesian product over instruments and properties ? – LightCZ Mar 02 '15 at 16:00
  • Generally: I am sure, that I would never go this way. I would load all the stuff lazily (see my link) and create DTO or serialize object. No explicit Fetching. Just `batch-fetching` to avoid 1 + N. To your question: - the above example I tested...and maybe ... there could be just one object fetched this way... so I updated the question. What it should show: we can use `Fetch` eager and `Future` for all collecitons... and `Future` for `SingleOrDefault` of the root entity. But I would use the `batch-fetching` – Radim Köhler Mar 02 '15 at 16:02
  • Yeah but i really want to load everything i need (only what i need) at one query to database because the data might be very live and change.... and that would mean i would have to setup pretty massive isolationMode - serializable to that session which have high performance hit on database. Also we dont have DTO in this case. This is only about service inner work and entity change. – LightCZ Mar 02 '15 at 16:06
  • Then what I tried to show, will work. Create as many Fetch, Eager, Future as many collecitons you have. Then put also root entity into Future and touch it. All will be loaded in single shot... I tested – Radim Köhler Mar 02 '15 at 16:07
  • Ok thanks, was kinda strange that futures are not hit when you execute normal query. I ll mark this as a answer. – LightCZ Mar 02 '15 at 16:09
  • Good luck with NHibernate ;) still great tool ;) – Radim Köhler Mar 02 '15 at 16:10