2

I am working with NHibernate right now, and am coming across a problem trying to improve the data bandwidth efficiency of what im working on.

The context is I am making a data event editor, which has a listview populated with events. These events have 3 types, each is stored in its own MSSQL Table.

There is a parent object, which contains lists of these 3 event types. Originally, I was just selecting all the parents and loading the events underneath it, but if you had say 20 parents, then it would run 60 querys or so to load the list.

I have changed it now to run just 3 querys, one to select all of each event type between date X and date Y. (This date is set with some DateTime pickers on the form).

Now, I am trying to print the name of the parent in the list too, but because I never visited the parent during the nHibernate, I only have a 'proxy' object and it causes a crash. The Parent object has potentially thousands of different objects under it, so I only want to load what I need.

What I have done in the meantime to get it to compile is to visit the object during my nHibernate.

My code to fetch the events is as follows:

public IList<MyEvent> GetAllMyEventForAllParentsFromSpecificTimePeriod(
    DateTime startTime, DateTime endTime)
{
    using (var session = SessionFactorySingleton.OpenSession())
    {
        var queryOver = session
               .QueryOver<MyEvent>()
               .Where(re => re.Time <= endTime)
               .And(re => startTime <= re.Time)
               .OrderBy(re => re.Time).Desc;

        var list = queryOver.List();

        if (list.Count > 0)
        {
            foreach (var myEvent in list.Select(myEvent => myEvent.ParentItem.Name)) { }
        }

        return list;
    }
}

What I have found so far is that the list loads much faster this way.

So I guess the real question here is what can I do to achieve the same thing as where I am visiting each and every ParentItem.Name just to force it to load them?

Updated Answer: I have managed to improve the efficiency thanks to a co-worker.

            return session
                .QueryOver<MyEvent>()
                .Where(re => re.Time <= endTime)
                .And(re => startTime <= re.Time)
                .JoinQueryOver(x => x.ParentItem)
                .List();

The Answer:

foreach (var parent in list.Select(x => x.ParentItem)) { NHibernateUtil.Initialize(parent); }

Which equates to:

public IList<MyEvent> GetAllMyEventForAllParentsFromSpecificTimePeriod(
    DateTime startTime, DateTime endTime)
{
    using (var session = SessionFactorySingleton.OpenSession())
    {
        var list = session
               .QueryOver<MyEvent>()
               .Where(re => re.Time <= endTime)
               .And(re => startTime <= re.Time)
               .List();

        foreach (var parent in list.Select(x => x.ParentItem)) { NHibernateUtil.Initialize(parent); }            

        return list;
    }
}
Cameron Stubber
  • 301
  • 3
  • 14
  • I our project, we stopped taking any entities out of the session. In other words, they are not used outside of transactions anymore. The data is packed into DTOs within the transaction. Lazy loading works like a charm in this case. Batch fetching is also a very important feature. – Stefan Steinegger Sep 29 '15 at 07:33

2 Answers2

0

If you just all the ParentItem names you can use a select.

var queryOver = session.QueryOver<Parent>()
               .Select(p => p.Name))
               .List<string>()
Colin Grealy
  • 615
  • 4
  • 12
  • This didnt seem to work. While queryOver did have a list of string names, it then caused my parent objects to become parents again. I think it has to be done from the 'list' variable I created to force them to be loaded. – Cameron Stubber Sep 29 '15 at 02:45
  • What do you mean " it then caused my parent objects to become parents again."? If you just want to force a proxy object to be loaded, you can call NHibernateUtil.Initialize on the event.Parent during the session, but that will cause the entire Parent object to be loaded. – Colin Grealy Sep 29 '15 at 02:57
  • Oops sorry, caused my parents to become Proxy's again. – Cameron Stubber Sep 29 '15 at 03:12
  • Did you try calling `NHibernateUtil.Initialize`? – Colin Grealy Sep 29 '15 at 03:44
  • Just tried it again with no change. NHibernateUtil.Initialize(list.Select(x => x.ParentItem)); – Cameron Stubber Sep 29 '15 at 04:51
  • 1
    That won't work. You need to call NHibernateUtil.Initialize on each ParentItem, i.e. `foreach (var parent in list.Select(x => x.ParentItem)) { NHibernateUtil.Initialize(parent); }` – Colin Grealy Sep 29 '15 at 21:01
0

As stated here Use NHibernate AliasToBean Transformer launch n+1 query, we have two options

  • use Projections
  • use Batch-Fetching

The first solution, would require some custom transformer:

1) How to partially project a child object with many fields in nHibernate

And to keep nice QueryOver API - here is how to create some Extension methods to support fully type aliasing:

2) Nhibernate Group By and Alias To Bean

The second solution (details here or there)

19.1.5. Using batch fetching

NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335