5

I'm developing a website with ASP.NET MVC, NHibernate and Fluent Hibernate and getting the error "no session or session was closed" when I try to access a child object.

These are my domain classes:

public class ImageGallery {
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
    public virtual IList<Image> Images { get; set; }
}

public class Image {
    public virtual int Id { get; set; }
    public virtual ImageGallery ImageGallery { get; set; }
    public virtual string File { get; set; }
}

These are my maps:

public class ImageGalleryMap:ClassMap<ImageGallery> {
    public ImageGalleryMap() {
        Id(x => x.Id);
        Map(x => x.Title);
        HasMany(x => x.Images);
    }
}

public class ImageMap:ClassMap<Image> {
    public ImageMap() {
        Id(x => x.Id);
        References(x => x.ImageGallery);
        Map(x => x.File);
    }
}

And this is my Session Factory helper class:

public class NHibernateSessionFactory {
    private static ISessionFactory _sessionFactory;
    private static ISessionFactory SessionFactory {
        get {
            if(_sessionFactory == null) {
                _sessionFactory = Fluently.Configure()
                    .Database(MySQLConfiguration.Standard.ConnectionString(MyConnString))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ImageGalleryMap>())
                    .ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"))
                    .BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }
    public static ISession OpenSession() {
        return SessionFactory.OpenSession();
    }
}

Everything works fine, when I get ImageGallery from database using this code:

IImageGalleryRepository igr = new ImageGalleryRepository();
ImageGallery ig = igr.GetById(1);

But, when I try to access the Image child object with this code

string imageFile = ig.Images[1].File;

I get this error:

Initializing[Entities.ImageGallery#1]-failed to lazily initialize a collection of role: Entities.ImageGallery.Images, no session or session was closed

Someone know how can I fix this?

Thank you very much!

Edit

My GetById method is:

    public ImageGallery GetById(int id) {
        using(ISession session = NHibernateSessionFactory.OpenSession()) {
            return session.Get<ImageGallery>(id);
        }
    }
2
  • You forgot to post the GetById method which retrieves the object from database and manipulates the session. Commented Mar 25, 2010 at 22:52
  • @MCardinale - I see this still doesn't have an accepted answer. Did you get around this? I'm running into the same problem and the answers below aren't helping. Commented Nov 8, 2011 at 23:36

3 Answers 3

8

Presumably, your GetById is closing the session. This could be explicit, or via a using statement.

A better approach to session management is the Open Session in View pattern. NHibernate sessions are cheap to create, so create one at the start of each request, and close it at the end.

// in global.asax.cs

public void Application_Start()
{
    BeginRequest += delegate {
        CurrentSessionContext.Bind( sessionFactory.OpenSession());
    };

    EndRequest += delegate {
        var session = sessionFactory.GetCurrentSession();
        if (null != session) {
            session.Dispose();
        }
        CurrentSessionContext.Unbind(sessionFactory);
    };
}

// in your NHibernateSessionFactory class
public static ISession OpenSession() {
    return SessionFactory.GetCurrentSession();
}

Using a DI container, we can have the session injected with instances scoped per-request.

// ninject example
Bind<ISession>()
    .ToMethod( ctx => sessionFactory.GetCurrentSession() )
    .InRequestScope();
Sign up to request clarification or add additional context in comments.

4 Comments

+1 for Open Session in View pattern, I couldn't remember the name.
One thing about that pattern that strikes me is that (depending on FlushMode) you could modify your entity in the View rendering code. Is that just the risk you take for the convenience of not projecting everything to DTOs?
Thank you very much. I asked another question (stackoverflow.com/questions/2525630) to somebody help do this, because I don't know how.
@LachlanRoche In your example here, when you're doing your binding in your DI container, where does sessionFactory come from? I'm struggling with this bit at the moment.
3

The session used to get the ig must be alive when accessing ig.Images[1]

I usually do this by instantiating the session before all repository calls, passing the session reference to the the repository constructor and using that reference inside the repository class

1 Comment

How and where do you instantiate session before repo calls? On the controller? And when do you close the session? Thank you!
2

I'm not sure if this applies here, but this is (or at least used to be) a common problem in Java MVC frameworks. It usually happens when create the session inside your action. When your action finishes executing, the session object goes out of scope and the session is closed/flushed. Then when the view tries to render the collection of objects you retrieved, it tries to load the lazy-loaded collection using a session that's been closed.

If you're using dependency injection in your project you could have your DI framework instantiate the session for you and pass it as constructor argument into your controller. You could also specify that the DI container should scope the NHibernate session to be the same as your HTTP Request. This will keep the session alive until the request ends (view has finished rendering).

I've used the Autofac as our DI container on a project and work and have been very happy with it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.