tags:

views:

98

answers:

1

I've just started using NHibernate, and I have some issues that I'm unsure how to solve correctly.

I started out creating a generic repository containing CUD and a couple of search methods. Each of these methods opens a separate session (and transaction if necessary) during the DB operation(s). The problem when doing this (as far as I can tell) is that I can't take advantage of lazy loading of related collections/objects.

As almost every entity relation has .Not.LazyLoad() in the fluent mapping, it results in the entire database being loaded when I request a list of all entities of a given type.

Correct me if I'm wrong, 'cause I'm still a complete newbie when it comes to NHibernate :)

What is most common to do to avoid this? Have one global static session that remains alive as long as the program runs, or what should I do?


Some of the repository code:

public T GetById(int id)
{
    using (var session = NHibernateHelper.OpenSession())
    {
        return session.Get<T>(id);
    }
}

Using the repository to get a Person

var person = m_PersonRepository.GetById(1); // works fine

var contactInfo = person.ContactInfo; // Throws exception with message:

// failed to lazily initialize a collection, no session or session was closed
+3  A: 

Your question actually boils down to object caching and reuse. If you load a Foo object from one session, then can you keep hold of it and then at a later point in time lazy load its Bar property?

Each ISession instance is designed to represent a unit of work, and comes with a first level cache that will allow you to retrieve an object multiple times within that unit of work yet only have a single database hit. It is not thread-safe, and should definitely not be used as a static object in a WinForms application.

If you want to use an object when the session under which it was loaded has been disposed, then you need to associate it with a new session using Session.SaveOrUpdate(object) or Session.Update(object).

You can find all of this explained in chapter 10 of the Hibernate documentation.

If this seems inefficient, then look into second-level caching. This is provided at ISessionFactory level - your session factory can be static, and if you enable second-level caching this will effectively build an in-memory cache of much of your data. Second-level caching is only appropriate if there is no underlying service updating your data - if all database updates go via NHibernate, then it is safe.

Edit in light of code posted

Your session usage is at the wrong level - you are using it for a single database get, rather than a unit of work. In this case, your GetById method should take in a session which it uses, and the session instance should be managed at a higher level. Alternatively, your PersonRepository class should manage the session if you prefer, and you should instantiate and dispose an object of this type for each unit of work.

public T GetById(int id)
{
    return m_session.Get<T>(id);
}

using (var repository = new PersonRepository())
{
    var person = repository.GetById(1);
    var contactInfo = person.ContactInfo;
} // make sure the repository Dispose method disposes the session.

The error message you are getting is because there is no longer a session to use to lazy load the collection - you've already disposed it.

David M
Thanks for your answer. I've read the chapter from the documentation, but I still don't quite understand what you mean in the 2nd sentence. Do you mean that I have to keep hold of the session if I need to access lazy loaded properties? I'll add some of my repository code to the question.
l3dx
Sure - add some context to the question and I'll address it more specifically in the answer.
David M
Nice, thanks a lot! +1 I'll refactor my repository then :) My understanding is that the session / db stuff should be "layered away" from the actual application, so If I in addition removes it from the repository, where would I place it?
l3dx
BTW. What about multiple repositories? If one of them holds a session, will that lead to problems if I need to operate on both?
l3dx
Hard to answer generically without knowing your application. One approach that works is to also "layer away" the domain objects themselves, since they are session-aware, and instead introducing some DTOs to transfer data to and from your UI. The method to get a DTO would then lazy load whatever it needs to, and you would manage your session instances at this level.
David M