2010-03-02 3 views
13

Я написал класс NHibernateSessionFactory, который содержит статический Nhibernate ISessionFactory. Это используется, чтобы убедиться, что у нас есть только одна фабрика сеансов, и в первый раз, когда вызывается OpenSession(), я создаю исполнительный SessionFactory - в следующий раз я использую то же самое и открываю на нем сеанс. Код выглядит так:Убедитесь, что NHibernate 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, но я не уверен, что делать. У кого-то была такая же проблема? Какое лучшее решение?

ответ

17

sessionFactory должен быть потокобезопасным синглетоном.

Общий шаблон в Java заключается в создании sessionFactory в статическом инициализаторе. См. HibernateUtil. Вы можете сделать то же самое в C#.

Есть другие patterns для реализации синглтона, включая использование блокировки или синхронизированных секций. Вот небольшой вариант, который должен решить вашу проблему, если я правильно ее понял.

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

Спасибо! Звучит разумно. Я просто добавил решение с помощью Mutex. Но вместо этого я должен использовать блокировку? – stiank81

+0

Но что, если мне нужно несколько соединений с базой данных одновременно? В настоящее время я не знаю другого способа обхода, чтобы создать несколько сессионных заводов ... – JustAMartin

+0

Как вы реализуете это в контроллерах? – Chazt3n

4

Я решил это с помощью 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);    
    } 
} 

Кажется, что это работает как шарм. Но стоит ли вместо этого использовать блокировку?

+2

Хм ... Похож на «блокировку с двойной проверкой», которая не является рекомендуемой практикой для реализации singleton (см. Эту страницу http: // www.yoda.arachsys.com/csharp/singleton.html). Кроме того, вместо мьютекса можно использовать простой замок. – ewernli

+0

Спасибо! Похоже, я должен пойти с простым замком. – stiank81

+0

Вы не должны использовать блокировку, вы должны использовать класс Monitor, по крайней мере. http://msdn.microsoft.com/en-us/library/x090d6tf.aspx –