Ensure NHibernate SessionFactory is only created once
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?
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();
}
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?
精彩评论