views:

421

answers:

2

I have written an NHibernateSessionFactory class which holds a static Nhibernate ISessionFactory. This is used to make sure we only have one session factory, and the first time OpenSession() is called I create the actuall SessionFactory - next times I use the same and open a session on it. The code looks like this:

public class NhibernateSessionFactory : INhibernateSessionFactory
{
    private static ISessionFactory _sessionFactory;

    public ISession OpenSession()
    {
        if (_sessionFactory == null)
        {
            var cfg = Fluently.Configure().
                Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")).
                Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>());
            _sessionFactory = cfg.BuildSessionFactory();
            BuildSchema(cfg);
        }
        return _sessionFactory.OpenSession();
    }

    private static void BuildSchema(FluentConfiguration configuration)
    {
        var sessionSource = new SessionSource(configuration);
        var session = sessionSource.CreateSession();
        sessionSource.BuildSchema(session);            
    }
}

Now I have a problem. My application is split between client and server. The Nhibernate stuff is on the server side. On startup both my client and server wants to access the database through some services which will use the NhibernateSessionFactory. Result is a race condition to whether the _sessionFactory is created before the request comes from the client. If it isn't it will fail..

I guess I need some sort of queueing or wait mechanism in the NhibernateSessionFactory, but I'm not sure about what to do. Anyone had the same problem before? What's the best solution?

+2  A: 

The sessionFactory must be a thread-safe singleton.

A common pattern in Java is to build the sessionFactory in a static initializer. See HibernateUtil. You can do the same in C#.

There are other patterns to implement singleton, including the usage of lock or synchronized sections. Here is slight variant that should solve your problem if I understood it correctly.

static readonly object factorylock = new object();

public ISession OpenSession()
{
    lock (factorylock)
    {
       if (_sessionFactory == null)
       {
            var cfg = Fluently.Configure().
               Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")).
               Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>());
            _sessionFactory = cfg.BuildSessionFactory();
            BuildSchema(cfg);
        }
    }
    return _sessionFactory.OpenSession();
}
ewernli
Thanks! Sounds reasonable. I just added a solution using Mutex. But I should use lock instead?
stiank81
+1  A: 

I solved this using a Mutex when creating the SessionFactory. Does this look reasonable:

public class NhibernateSessionFactory : INhibernateSessionFactory
{
    private static ISessionFactory _sessionFactory;
    private static Mutex _mutex = new Mutex();  // <-- Added

    public ISession OpenSession()
    {
        if (_sessionFactory == null)
        {
            _mutex.WaitOne();              // <-- Added
            if (_sessionFactory == null)   // <-- Added
            {                              // <-- Added
                var cfg = Fluently.Configure().
                    Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")).
                    Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>());
                _sessionFactory = cfg.BuildSessionFactory();
                BuildSchema(cfg);
            }                              // <-- Added
            _mutex.ReleaseMutex();         // <-- Added

        }
        return _sessionFactory.OpenSession();
    }

    private static void BuildSchema(FluentConfiguration configuration)
    {
        var sessionSource = new SessionSource(configuration);
        var session = sessionSource.CreateSession();
        sessionSource.BuildSchema(session);            
    }
}

Seems to work like a charm. But should I use lock instead?

stiank81
Hmm...Looks like the "double-check locking", which is not a recommended practice to implement singleton (see this page http://www.yoda.arachsys.com/csharp/singleton.html). Also, a plain lock can be used instead of a mutex.
ewernli
Thanks! Sounds like I should go with the plain lock instead.
stiank81