Loading complex graph in nhibernate

2019-07-30 06:55发布

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)

1条回答
做个烂人
2楼-- · 2019-07-30 07:21

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.

查看更多
登录 后发表回答