0

У меня есть простой репозиторий, который извлекает некоторые данные с помощью EF6. Я также использую структуру DI для инъекции зависимостей.Как украсить репозиторий только для чтения репозиторием аудита при использовании UnitOfWork?

namespace Domain 
{ 
    public interface IMyRespository 
    { 
     List<MyObject> FetchObjects(); 
    } 
} 

namespace Data 
{ 
    public class MyRepository : IMyRepository 
    { 
     private readonly MyDbContext _context; 

     public MyRepository(MyDbContext context) 
     { 
      _context = context; 
     } 

     public List<MyObjects> FetchObjects() 
     { 
      return _context.MyObjects.ToList(); 
     } 
    } 
} 

Новое требование утверждает, что мне нужно регистрировать каждый вызов FetchObjects() и его выходы. Я думал, что это прекрасный пример применения шаблона Decorator.

namespace Domain 
{ 
    public class MyRepositoryDecorator : IMyRepository 
    { 
     private readonly IMyRepository _inner; 
     private readonly ILogRepository _logRepository; 

     public MyRepositoryDecorator(IMyRepository inner, ILogRepository logRepository) 
     { 
      _inner = inner; 
      _logRepository = logRepository; 
     } 

     public List<MyObjects> FetchObjects() 
     { 
      var objects = _inner.FetchObjects(); 
      var logObject = new LogObject(objects); 
      _logRepository.Insert(logObject); 
      _logRepository.Save(); 
      return objects; 
     } 
    } 
} 

Теперь я ищу, чтобы использовать шаблон UnitOfWork, и я не уверен, как реализовать в этом случае.

Как я понимаю, какой-то компонент должен управлять UnitOfWork. Таким образом, в этом случае класс обслуживания будет делать некоторые вызовы и в конце вызова Save/Commit в классе UnitOfWork.

Однако, если интерфейс репозитория указывает на действие только для чтения, нет причин для того, чтобы класс обслуживания обернул вызов в модуле UnitOfWork и вызывается Save/Commit в конце. Это выглядело бы очень странно. Однако декоратор требует, чтобы это выполняло свою работу.

У меня, вероятно, отсутствует какая-то существенная конструкция. Любые идеи о том, как правильно подойти к этому сценарию?

ответ

0

Было бы плохой идеей смешать UoW с репозиторием с помощью Decorator (или аналогичного) просто потому, что UoW не редкость распространяться на несколько репозиториев.

Также Репозиторий не может решить, должно ли быть совершено UoW или нет. Хранилища должны знать как можно меньше о UoWs, в идеале (и в большинстве случаев это не так).

В вашем сценарии класс UnitOfWork будет в значительной степени только обрабатывает транзакции, поэтому она может быть реализована в виде простой обертки вокруг TransactionScope, что-то вроде:

public sealed class UnitOfWork : IDisposable { 
    private readonly TransactionScope _transaction; 
    public UnitOfWork() { _transaction = new TransactionScope(); } 

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

Сейчас на сервис для создания экземпляра/совершить UOW, не до Repository:

//assuming in a service 
public void DoSomething() { 
    using(var uow = new UnitOfWork()) { 
     _repositoryA.UpdateSomething(); 
     _repositoryB.DeleteSomething(); 
     _uow.Commit(); 
    } 
} 

И если ваш сервис только хочет читать данные, то просто не использовать UnitOfWork в этой операции (или использовать его без вызова Commit так что это будет просто выбрасывать).

Если ваш репозиторий должен знать о UoW, он, как правило, будет передан как другой параметр в свой метод поведения. Обратите внимание, что это не сделано, потому что репозиторий хочет вызвать Commit, но иногда (редко) он необходим, чтобы репозиторий «заручился» с UoW. Эти случаи довольно сложны.

+0

Спасибо за ответ. Я не думаю, что репозиторий-декоратор обязательно должен знать о UoW. Я просто не уверен, что декоратор может сделать предположение, что он всегда завернут в UoW. Проблема заключается в том, что если вызывающая служба не настроит UoW (потому что с ее точки зрения это просто чтение данных), Commit никогда не вызывается, а изменения, сделанные декоратором, теряются. – batkuip

+0

Репозиторий не знает ни о каком UoW и всегда выполняет свои внутренние коммиты в каждом методе в этом сценарии. Но если служба не передает UoW, транзакция по всем репозиториям будет отменена. Если служба не использует UoW и вызывает множественные действия в одном или нескольких репозиториях, существует вероятность того, что некоторые операции будут совершены, а некоторые не будут в случае, если они не пройдут между ними. Но именно поэтому нам нужен UoW в качестве шаблона. –

+0

Также всегда хорошо следовать принципу CQS (сформулированном Б.Meyer ~ 20 лет назад), в котором говорится, что если ваш метод возвращает какое-либо состояние, то это Query, и этот метод не должен вносить никаких изменений. И если ваш метод вносит изменения, то это команда, и она не должна возвращать какое-либо состояние (должно возвращать пустоту практически). Если вы будете следовать этому простому принципу, тогда не будет никаких «с его точки зрения, это просто чтение», это будет ясно с самого начала: если вы делаете команды, то используйте UoW, если вы только делаете запросы, тогда не делайте этого. –