views:

71

answers:

1

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();
                }
            }

        }
}
+2  A: 

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:

http://stackoverflow.com/questions/2362195/ensure-nhibernate-sessionfactory-is-only-created-once

Daniel Dyson
By using it like above though will that not create an instance of the factory for each crud operation?
Andi
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
Also, you can test whether a new factory is created by putting a breakpoint on the line after: if (_sessionFactory == null)
Daniel Dyson
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(); } }
Andi
Not from me mate?? Was just thinking the same?????
Andi
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
How would this cope if Add(Login login) was called within Add(User user)? As it would already have an open session?
Andi
In that case, I would overload the Add(Login login). I'll update my answer to show you
Daniel Dyson
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 :)
Andi
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
In the Add(Login login, ISession session) method would i use somethign likeusing(session){ //code }
Andi
no. the using statement will close the session when it has finished. Just call session.Save(login);
Daniel Dyson
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?
Andi
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
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
Will drop you an email now (watch out for your junk mail I've been having problems, it will come from @zero7web.com)
Andi
Any ideas on best practise for this? Thanks for help
Andi