Убедитесь, что 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);            
    }
}

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