C# 如何在使用UnitOfWork时使用audit存储库装饰只读存储库?

C# 如何在使用UnitOfWork时使用audit存储库装饰只读存储库?,c#,dependency-injection,repository,repository-pattern,unit-of-work,C#,Dependency Injection,Repository,Repository Pattern,Unit Of Work,我有一个简单的存储库,它使用EF6获取一些数据。我还使用DI框架来注入依赖项 namespace Domain { public interface IMyRespository { List<MyObject> FetchObjects(); } } namespace Data { public class MyRepository : IMyRepository { private readonly M

我有一个简单的存储库,它使用EF6获取一些数据。我还使用DI框架来注入依赖项

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();
         }
     }
} 
命名空间域
{
公共接口imyresposition
{
列出获取对象();
}
}
命名空间数据
{
公共类MyRepository:IMyRepository
{
私有只读MyDbContext\u context;
公共MyRepository(MyDbContext上下文)
{
_上下文=上下文;
}
公共列表获取对象()
{
return_context.MyObjects.ToList();
}
}
} 
一个新的要求是,我需要记录每个FetchObjects()调用及其输出。我认为这将是应用装饰模式的完美例子

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;
        }
    }
}
命名空间域
{
公共类MyRepositoryDecorator:IMyRepository
{
私有只读存储库(内部);
私有只读iLogpository存储库;
公共MyRepositoryDecorator(IMyRepository内部、iLogository日志存储库)
{
_内部=内部;
_logRepository=logRepository;
}
公共列表获取对象()
{
var objects=_inner.FetchObjects();
var logObject=新logObject(对象);
_logRepository.Insert(logObject);
_logRepository.Save();
归还物品;
}
}
}
现在我希望采用UnitOfWork模式,我不确定在这种情况下如何实现

据我所知,某些组件需要管理UnitOfWork。因此,在这种情况下,服务类将进行一些调用,并在最后调用UnitOfWork类上的Save/Commit

但是,如果存储库接口指示只读操作,则服务类没有理由将调用包装在UnitOfWork中,并在最后调用Save/Commit。这看起来也很奇怪。然而,装饰师需要这个来完成它的工作


我可能遗漏了一些基本的构造。有没有关于如何正确处理此场景的想法?

使用Decorator(或类似工具)将UoW与存储库混合使用是个坏主意,因为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取决于服务,而不是取决于存储库:

//assuming in a service
public void DoSomething() {
    using(var uow = new UnitOfWork()) {
        _repositoryA.UpdateSomething();
        _repositoryB.DeleteSomething();
        _uow.Commit();
    }
}
如果您的服务只想读取数据,那么就不要在该操作中使用UnitOfWork(或者在不调用Commit的情况下使用UnitOfWork,这样它就会被释放)

如果您的存储库需要了解UoW,它通常将作为其行为方法中的另一个参数传递。
请注意,之所以不这样做是因为存储库希望调用Commit,但有时(很少)存储库需要“登记”到UoW。这些案例相当复杂。

谢谢您的回复。我认为decorator存储库不一定需要了解UoW。我只是不确定一个装饰师是否能假设它总是被包装在UoW中。问题似乎是,如果调用服务不设置UoW(因为从其角度来看,它只是在读取数据),则不会调用Commit,并且装饰程序所做的更改也会丢失。存储库不知道任何UoW,并且在这种情况下,它总是在每个方法中进行内部提交。但是,如果服务没有提交UoW,那么涉及的所有存储库中的事务都将回滚。如果服务不使用UoW并在一个或多个回购协议上调用多个行为,则可能会提交一些操作,如果在这两个操作之间失败,则不会提交一些操作。但这正是我们需要UoW作为模式的原因。同时,遵循CQS原则(由B.Meyer在20年前制定)总是很好的,该原则表示,如果您的方法返回任何状态,那么它就是一个查询,并且该方法不得进行任何更改。如果您的方法进行了更改,那么它是一个命令,不能返回任何状态(实际上应该返回void)。如果你遵循这个简单的原则,那么就不会有任何“从它的角度来看它只是阅读”,从一开始就很清楚:如果你发出命令,那么使用UOW,如果你只做查询,那么不要这样做。如果它是业务逻辑的一部分,那么它显然不应该存在。但是,如果这只是一个“技术问题”,那么好吧,就在内部抑制日志操作的事务。遵循SRP并将日志记录专用于另一个“日志记录服务”而不是“就地”进行记录总是好的。该服务将知道如何在编写日志时处理内容(禁止事务,无论什么)。我理解基本原则,很高兴看到它们结合在一起。我本来希望能够装饰一下存储库,但是CQS把它扔出了窗外。这很有道理。在数以百万计的日志装饰器示例中,没有人演示如何记录“查询”,这就解释了这一点。它不仅仅是“技术性的”,我实际上需要它作为一个业务逻辑。所以在这种情况下,我应该用ILogService替换ILogrespository并保留装饰器?(使用UoW实现ILogService)