l3dx
l3dx

Reputation: 2955

NHibernate sessions - what's the common way to handle sessions in windows applications?

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

Upvotes: 0

Views: 512

Answers (1)

David M
David M

Reputation: 72870

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.

Upvotes: 3

Related Questions