Правильное использование шаблона Unibernate Unit Of Work и Ninject

У меня есть следующая реализация, и мне хотелось бы получить некоторую обратную связь относительно того, правильно ли она использует NHibernate для сеансов и транзакций.

public interface IUnitOfWork : IDisposable
{
    ISession CurrentSession { get; }
    void Commit();
    void Rollback();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        CurrentSession = _sessionFactory.OpenSession();
        _transaction = CurrentSession.BeginTransaction();
    }

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

    public void Commit()
    {
        _transaction.Commit();
    }

    public void Rollback()
    {
        if (_transaction.IsActive) _transaction.Rollback();
    }
}

Связывание с Ninject

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();

Вот пример использования:

public class Repository : IRepository
{
    private readonly ISessionFactory _sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void Add(IObj obj)
    {
        using (var unitOfWork = new UnitOfWork(_sessionFactory))
        {
            unitOfWork.CurrentSession.Save(obj);
            unitOfWork.Commit();
        }         
    }
}

В моей предыдущей реализации я бы ввел IUnitOfWork в свой конструктор репозитория, например:

public Repository(IUnitOfWork unitOfWork)
    {...

Но метод Dispose() не будет выполняться, вызвав последующий вызов для исключения этого исключения: "Невозможно получить доступ к удаленному объекту. Имя объекта:" AdoTransaction ".

Ответы

Ответ 1

Первое наблюдение: ваш репозиторий не должен фиксировать единицу работы. Это наносит ущерб всей точке шаблона работы. Немедленно сохраняя свои изменения внутри репозитория, вы "микроуправляете" сеанс NHibernate.

На единицу работы следует ссылаться выше в стеке, на вашем уровне приложения/услуги. Это позволяет вам иметь код приложения, который выполняет несколько действий, потенциально в разных хранилищах, и все еще в конце передает все сразу.

Сам класс UnitOfWork выглядит хорошо, хотя вы должны спросить себя, действительно ли он вам нужен. В NHibernate ISession - это ваша единица работы. Ваш класс UnitOfWork, похоже, не добавляет большого значения (особенно, поскольку вы все равно подвергаете свойству CurrentSession)

Но вам нужно подумать об этом всю жизнь. Я думаю, что вы ошибаетесь в этом вопросе. Управление жизненным циклом сеанса зависит от типа приложения, которое вы разрабатываете: в веб-приложении вы обычно хотите иметь единицу работы для каждого запроса (вы можете захотеть google на "сеансе nhibernate для каждого запроса" ). В настольном приложении это несколько усложняется, вы в большинстве случаев хотите "сеанс на экран" или "разговор за транзакцию".

Ответ 2

У меня есть приложение типа CRUD в основном, и я реализовал шаблон работы с репозиторием, но не смог уйти от разделения сеанса/транзакции. Сеансам и транзакциям нужны разные сроки жизни. В настольном мире сеанс обычно "за экран", а транзакция - "за действие пользователя".

Дополнительная информация в отличная статья.

Итак, в чем я оказался:

  • IUnitOfWork → Завершает сеанс, реализует IDisposable
  • IAtomicUnitOfWork → Обертывает транзакцию, реализует IDisposable
  • IRepository → Обеспечивает получение, сохранение, удаление и запрос доступа

Я сделал так, чтобы вам понадобилось IUnitOfWork для сборки IAtomicUnitOfWork, и вам нужно IAtomicUnitOfWork построить IRepository, чтобы обеспечить правильное управление транзакциями. Это действительно все, что я приобрел, реализуя свои собственные интерфейсы.

Как сказал jeroenh, вы почти просто используете ISession и ITransaction, но в конце концов я почувствовал себя немного лучше, написав весь свой код против интерфейса, который я определил.

Ответ 3

Важная часть ответа заключается в том, что вы хотите, чтобы ваши размеры транзакций были. Прямо сейчас (как показал jeroenh) транзакция - это вызов метода в вашем репозитории. Это очень мало и, вероятно, не нужно. Я создал приложение ASP.MVC и использует размер транзакции, включающий все, от одного HTTP-запроса. Это может быть несколько чтения/обновления базы данных. Я использую ту же единицу работы и Ninject для IOC. Посмотрите, возможно, что-то поможет в ваших проблемах:

http://bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/

http://bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/

http://bobcravens.com/2010/09/the-repository-pattern-part-2/

http://bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/

Надеюсь, что это поможет.

Боб