Правильное использование шаблона 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/
Надеюсь, что это поможет.
Боб