Класс репозитория NHibernate — 1. Обрабатывается транзакцией | 2. Как инициализировать сеанс

#nhibernate #session #transactions #repository

#nhibernate #сеанс #транзакции #репозиторий

Вопрос:

Привет, я начинаю изучать NHibernate и теперь пытаюсь создать собственный класс репозитория для NHibernate.

Я нашел много статей об этой теме. Я выбрал этот учебник: http://dotnetslackers.com/Community/blogs/antrad/archive/2008/10/25/about-repository-pattern-and-nhibernate.aspx

Я немного смущен тем, как обрабатывать транзакцию в репозитории.

У меня есть интерфейс репозитория:

 public interface IRepository<T> where T : class
{
    void Commit();
    void RollBack();
    void BeginTransaction();
    //...
}
  

Мой вопрос в том, почему хорошо начинать транзакцию в классе конструктора репозитория.

Эта часть кода взята из блога сверху.

 public class NHibRepository<T> :IRepository<T> 
    where T : class
{
    #region Private fields

    private ISession _session;

    #endregion

    #region Constructor

    public NHibRepository(ISession session)
    {
        _session = NHibernateSessionManager.Instance.GetSession();

        //why begin transaction where???
        _session.BeginTransaction();
    }

    #endregion


    #region Trans

    public void Commit()
    {
        if (session.Transaction.IsActive)
        {
            session.Transaction.Commit();
        }
    }

    public void Rollback()
    {
        if (session.Transaction.IsActive)
        {
            session.Transaction.Rollback();
            session.Clear();
        }
    }

    public void BeginTransaction()
    {
        Rollback();
        session.BeginTransaction();
    }

    #endregion


}
  

Я обновляю этот репозиторий и теперь использую это решение:

 public class NHibRepository<T> :IRepository<T> 
        where T : class
    {
        #region Private fields

        private ISession _session;

        #endregion

        #region Constructor

        public NHibRepository(ISession session)
        {
            _session = session;
        }

        #endregion


        #region Trans

        public void Commit()
        {
            using (var trans=_session.BeginTransaction())
            {
                trans.Commit();
            }
        }

        public void RollBack()
        {
            using (var trans = _session.BeginTransaction())
            {
                trans.Rollback();
            }
        }

        public void BeginTransaction()
        {
            throw new NotImplementedException();
        }

        #endregion


    }
  

Мой первый вопрос: какое решение лучше и почему? или что правильно, почему?


И мой второй вопрос заключается в том, как открыть сеанс для класса репозитория, теперь я использую этот вспомогательный класс:

   public class NHibeHelper
    {
        private static ISessionFactory _sessionFactory;

        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                    InitializeSessionFactory();

                return _sessionFactory;
            }
        }

        private static void InitializeSessionFactory()
        {

            _sessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                              .ConnectionString(
                                   @"Server=ZuesSQLEXPRESS;Database=TEST;Trusted_Connection=True;")
                                  )

                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>().Conventions.Add(DefaultCascade.All()))


                .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))

                .BuildSessionFactory();
        }

        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
  

При использовании подобным образом:

  _userRepo = new NHibRepository<User>(NHibeHelper.OpenSession());
  

Ответ №1:

Рассматривая два варианта, которые вы представили для управления транзакциями, они оба на самом деле имеют проблемы.

В примере 1 транзакция не обязательно удаляется, а также вы связываете время жизни транзакции с временем жизни репозитория. Это не обязательно плохо, но если вы не используете контейнер IoC или какой-либо другой метод для обеспечения повторного создания репозитория для каждой операции с базой данных, вы сможете выполнить фиксацию только один раз, а последующие коммиты, вероятно, завершатся неудачей.

К сожалению, пример 2 не будет работать вообще — при фиксации / откате вы каждый раз запускаете новую транзакцию. Особенно в случае отката, если это было вызвано из внешнего кода в контексте транзакции, все, что вы сделаете, это запустите новую вложенную транзакцию, а затем немедленно откатите ее. К сожалению, не очень полезно.

Мое личное решение вашего первого вопроса — вывести управление транзакциями из репозитория, используя внешние транзакции в системе.Транзакции. Таким образом, у вас все еще есть разделение, поскольку внешние транзакции не зависят от поставщика, но также у вас есть больше контроля над тем, какие из многих операций репозитория, которые вы можете выполнять, будут задействованы в транзакции, а не всегда использовать модель с одной транзакцией на операцию. Смотрите http://msdn.microsoft.com/en-us/magazine/cc163527.aspx для получения дополнительной информации.

Что касается вашего второго вопроса, я бы настоятельно рекомендовал заглянуть в контейнер IoC, такой как Castle Windsor или Unity. Вспомогательный класс, который у вас есть, намного лучше, чем перестраивать фабрику сеансов и тому подобное каждый раз, но, используя контейнер IoC, вы можете очень легко обеспечить, чтобы время жизни вашего объекта сеанса было именно таким, каким вы хотите. Например, в ряде служб WCF для каждого вызова, над которыми я работаю, я использую Castle Windsor, чтобы гарантировать, что сеанс создается (и вводится в репозиторий), когда начинается операция WCF, и удаляется (вместе с репозиторием), когда она заканчивается.