Мой вариант реализации Unit Of Work, применительно к NHibernate.
1. Интерфейс IUnitOfWork:
1. Интерфейс IUnitOfWork:
public interface IUnitOfWork : IServiceProvider, IDisposable { void Commit(); }
- Лишь один метод Commit, больше ничего не нужно. Добавление метода Rollback может привести к тому, что в результате ошибки мы можем попасть в третье состояние, когда ни Commit ни Rollback не были вызваны.
- Никаких методов Add/Remove/Save/Get etc. Это не является ответственностью UoW.
- Наследование от IServiceProvider/IDisposable нужно лишь в случае, если не используется IoC контейнер с поддержкой nested lifetime scope.
2. Интерфейс IServiceProvider:
public interface IServiceProvider { IRepositoryGetRepository (); }
- Как я уже сказал он нужен только в случае отсутствия нормального IoC контейнера.
- Нужен для получения scoped сервисов, чье время жизни будет связано с UoW.
- По хорошему метод должен быть таким:
T GetService();
но я решил упростить пример :)
3. Интерфейс IRepository:
public interface IRepository{ T Get(int id); void Add(T obj); void Delete(T obj); }
- Вот он то и отвечает за все получение/сохранение объектов
- Бросается в глаза отсутствие метода Save :)
4. Пример использования:
var uowFactory = new UnitOfWorkFactory(); int catId; using (var uow = uowFactory.CreateUnitOfWork()) { var repository = uow.GetRepository<Cat>(); var cat = new Cat { Name = "Chalky", Age = 3 }; repository.Add(cat); uow.Commit(); catId = cat.Id; } using (var uow = uowFactory.CreateUnitOfWork()) { var repository = uow.GetRepository<Cat>(); var cat = repository.Get(catId); cat.Age = 4; uow.Commit(); }
5. Реализация:
Если есть желание сэкономить на спичках, то можно задуматься о lazy создании транзакции, чтобы UoW с no-op не приводил к открытию соединения.
internal sealed class UnitOfWork : IUnitOfWork { private readonly ISession _session; private readonly ITransaction _transaction; public UnitOfWork(ISession session) { _session = session; _transaction = session.BeginTransaction(); } public void Commit() { if (!_session.IsOpen)
{
throw new InvalidOperationException("Session is already closed!");
}
_transaction.Commit();
_session.Close(); } public IRepositoryGetRepository _session.Dispose();() { return new Repository (_session); } public void Dispose() { try {
} catch { // TODO: log exception } } }
Если есть желание сэкономить на спичках, то можно задуматься о lazy создании транзакции, чтобы UoW с no-op не приводил к открытию соединения.
internal sealed class Repository: IRepository { private readonly ISession _session; public Repository(ISession session) { _session = session; } public T Get(int id) { return _session.Get (id); } public void Add(T obj) { _session.Save(obj); } public void Delete(T obj) { _session.Delete(obj); } } public sealed class UnitOfWorkFactory { private readonly ISessionFactory _sessionFactory; public UnitOfWorkFactory() { // TODO: //_sessionFactory = configuration.BuildSessionFactory(); } public IUnitOfWork CreateUnitOfWork() { return new UnitOfWork(_sessionFactory.OpenSession()); } } public sealed class Cat { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }