Убедитесь, что NHibernate SessionFactory создается только один раз
Я написал класс NHibernateSessionFactory, который содержит статический Nhibernate ISessionFactory. Это используется, чтобы убедиться, что у нас есть только один сеанс factory, и при первом вызове OpenSession() я создаю исполнительный SessionFactory - в следующий раз я использую то же самое и открываю на нем сеанс. Код выглядит следующим образом:
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);
}
}
Теперь у меня проблема. Мое приложение разделено между клиентом и сервером. Материал Nhibernate находится на стороне сервера. При запуске мой клиент и сервер хотят получить доступ к базе данных через некоторые службы, которые будут использовать NhibernateSessionFactory. Результатом является условие гонки, связанное с созданием _sessionFactory до того, как запрос поступит от клиента. Если это не так, это провалится.
Я думаю, мне нужен какой-то механизм ожидания или ожидания в NhibernateSessionFactory, но я не уверен, что делать. У кого-то была такая же проблема? Какое лучшее решение?
Ответы
Ответ 1
sessionFactory
должен быть потокобезопасным синглетоном.
Общим шаблоном в Java является создание sessionFactory
в статическом инициализаторе. См. HibernateUtil. Вы можете сделать то же самое в С#.
Существуют другие шаблоны для реализации singleton, включая использование блокировки или синхронизированных разделов. Вот небольшой вариант, который должен решить вашу проблему, если я правильно ее понял.
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();
}
Ответ 2
Я решил это, используя Mutex при создании SessionFactory. Это выглядит разумно:
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);
}
}
Кажется, он работает как шарм. Но стоит ли вместо этого использовать блокировку?