3

I currently have a NHibernateHelper class which creates a session factory. I keep recieving errors saying 'Session Closed!'. Am I going about this in the wrong way? The error occurs when I call the Add(Login login) which gets called after Add(User user)

public class NHibernateHelper
{
    private static ISessionFactory _sessionFactory;

    private static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {
                var configuration = new Configuration();
                configuration.Configure();
                configuration.AddAssembly("System.Core");
                _sessionFactory = configuration.BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }

    public static ISession OpenSession()
    {
        return SessionFactory.OpenSession();
    }
}

Here is my repository:

internal class UserRepository : IUserRepository
{
    private ISession _db = NHibernateHelper.OpenSession();

    public void Add(User user)
        {
            using (_db)
            {
                using (ITransaction transaction = _db.BeginTransaction())
                {
                    IEnumerable<UserRole> userRoles = user.UserRoles;
                    user.UserRoles = null;
                    _db.Save(user);
                    foreach (UserRole userRole in userRoles)
                    {
                        userRole.UserID = user.UserID;
                        _db.Save(userRole);
                    }
                    transaction.Commit();
                }
            }
        }



        public void Add(Login login)
        {
            using (_db)
            {
                using (ITransaction transaction = _db.BeginTransaction())
                {
                    _db.Save(login);
                    transaction.Commit();
                }
            }

        }
}
Andrew
  • 9,967
  • 10
  • 64
  • 103

1 Answers1

2

This is because you are calling using(_db), which closes the session at the end of the block.

Rather than having the variable _db just call OpenSession for each operation

    public void Add(User user)
    {
        using (ISession session = NHibernateHelper.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                IEnumerable<UserRole> userRoles = user.UserRoles;
                user.UserRoles = null;
                session.Save(user);
                foreach (UserRole userRole in userRoles)
                {
                    userRole.UserID = user.UserID;
                    session.Save(userRole);
                }
                transaction.Commit();
            }

        }
    }

UPDATE:

   public void Add(Login login)
   {
        using (ISession session = NHibernateHelper.OpenSession())
        {
             Add(login, session);
        }
   }

   public void Add(Login login, ISession session)
   {
        //no longer need to create a session here - it is passed in
        //using (ISession session = NHibernateHelper.OpenSession()) 

        ...Add using the session
   }

It is the creation of the factory that is expensive, so your Helper is a good thing to use. Opening the session is a cheap operation, so no need to have a shared session like this.

Here is a link to another SO question that shows how to make your factory helper threadsafe:

Ensure NHibernate SessionFactory is only created once

Community
  • 1
  • 1
Daniel Dyson
  • 13,192
  • 6
  • 42
  • 73
  • By using it like above though will that not create an instance of the factory for each crud operation? – Andrew Aug 24 '10 at 08:45
  • No, the Factory is a static singleton. It is the ISession that will be created for each operation. See my edit for the reason that the _db is being closed. - First line of my answer. – Daniel Dyson Aug 24 '10 at 08:47
  • Also, you can test whether a new factory is created by putting a breakpoint on the line after: if (_sessionFactory == null) – Daniel Dyson Aug 24 '10 at 09:11
  • I'm confused as _sessionFactory never appears to be null even on the first call! I'm used to having a data context and declaring something at the top like _db. So for a query I end up with something like this now then: using (NHibernateHelper.OpenSession()) { using (ITransaction transaction = NHibernateHelper.OpenSession().BeginTransaction()) { NHibernateHelper.OpenSession().Save(user); transaction.Commit(); } } – Andrew Aug 24 '10 at 09:48
  • I am such an idiot! I deserved that downvote. See my edited answer. Sorry I didn't test my original answer. Hope I didn't waste too much of your time. – Daniel Dyson Aug 24 '10 at 10:32
  • How would this cope if Add(Login login) was called within Add(User user)? As it would already have an open session? – Andrew Aug 25 '10 at 08:41
  • In that case, I would overload the Add(Login login). I'll update my answer to show you – Daniel Dyson Aug 25 '10 at 08:57
  • OK thanks, so there isn't a way of doing something similar from the Helper level to keep my repo's a bit cleaner? Appreciate your support btw :) – Andrew Aug 25 '10 at 10:20
  • That certainly is a design coinsideration that you could explore. You could create a specific manager class that gets a session from the NHibernateHelper and coordinates the saving of the different entities within the same session transaction. But this is still part of your repository. By the way this should be in a separate class to the NHibernateHelper which is a good small class and doesn't need any more functionality. – Daniel Dyson Aug 25 '10 at 10:40
  • In the Add(Login login, ISession session) method would i use somethign like using(session){ //code } – Andrew Aug 25 '10 at 11:56
  • no. the using statement will close the session when it has finished. Just call session.Save(login); – Daniel Dyson Aug 25 '10 at 12:09
  • Looking at the way session's are passed around like above would I not have a similar issue with the transactions being nested or is that ok to do so? – Andrew Aug 25 '10 at 13:50
  • 1
    You will only want one transaction per group of related activities. If you are performing multiple changes, create the transation straight after the call to OpenSession, as you do in the first code block in your question and then commit it just before the closing brace as you have done. – Daniel Dyson Aug 25 '10 at 14:18
  • 1
    When I get a chance (i.e. when I get home), I will put together a sample app. Send me an email and we can set up a dialog offline, and perhaps post the results here as a new answer later. – Daniel Dyson Aug 25 '10 at 14:20
  • Will drop you an email now (watch out for your junk mail I've been having problems, it will come from @zero7web.com) – Andrew Aug 25 '10 at 14:39
  • Any ideas on best practise for this? Thanks for help – Andrew Aug 26 '10 at 13:26