C# 在工作单元模式中重用服务调用

C# 在工作单元模式中重用服务调用,c#,.net,design-patterns,dependency-injection,unit-of-work,C#,.net,Design Patterns,Dependency Injection,Unit Of Work,我有一个使用WebApi、通用存储库、EF6和工作单元模式的场景 (以便将多个调用的所有更改包装到同一上下文。) 管理器层用于执行对不同存储库以及其他管理器的调用 目前,客户经理确实注入了回购协议和其他经理,如: public class CustomerManager { public CustomerManager(IRepository<Customer> _customerRepository, IRepository<Order> orderRepos

我有一个使用WebApi、通用存储库、EF6和工作单元模式的场景 (以便将多个调用的所有更改包装到同一上下文。)

管理器层用于执行对不同存储库以及其他管理器的调用

目前,客户经理确实注入了回购协议和其他经理,如:

public class CustomerManager  {
    public CustomerManager(IRepository<Customer> _customerRepository, IRepository<Order> orderRepository, IManager itemManager) {
        _orderReporsitory = orderReporsitory;
        _itemManager = itemManager;
        _customerRepository = customerRepository;
}

    public bool Save(Customer customer) {
        _orderReporsitory.Find...
        _itemManager.IsItemUnique(ItemId)
        _customerRepository.Save(customer);
    }
}
公共类CustomerManager{
公共CustomerManager(IRepository _customerRepository,IRepository orderRepository,IManager itemManager){
_OrderReportory=OrderReportory;
_itemManager=itemManager;
_customerRepository=customerRepository;
}
公共布尔保存(客户){
_订单回复。查找。。。
_itemManager.IsItemUnique(ItemId)
_customerRepository.Save(客户);
}
}
此代码不编译,仅供参考

像这样的方法

将多个存储库包装在一个工作单元下,并将所有更改一起刷新

我的问题还涉及添加另一个管理器层,该层也将被包装在工作单元中,允许对存储库和其他管理器进行调用 (因为我想重用一些管理器逻辑。就像在示例中一样,我正在重用一些ItemManager逻辑)

此代码

使用(var uow=new UnitOfWork())
{
var catService=new Services.CategoryService(uow);
var custService=new Services.CustomerService(uow);
var cat=new Model.Category{Name=catName};
catService.Add(dep);
Add(newmodel.Customer{Name=custName,Category=cat});
uow.Save();
}
正在使用类似于我需要的东西,但我也希望能够注入服务来对它们进行单元测试(而不是在我的管理器/服务方法主体中创建实例)

最好的方法是什么


谢谢

您的工作单元代码片段有几个问题,例如:

  • 您可以在该方法中显式地创建和处理工作单元,迫使您将该工作单元从一个方法传递到另一个方法,从一个类传递到另一个类
  • 这会导致违反,因为您现在依赖于具体类型(
    CategoryService
    CustomerService
    ),这会使代码复杂化,并使代码更难测试
  • 如果需要更改工作单元的创建、管理或处置方式,则必须在整个应用程序中进行全面更改;违反法律的行为
我在中更详细地表达了这些问题

相反,我建议使用一个
DbContext
,通过完整的请求共享它,并在应用程序的基础结构中控制它的生存期,而不是在整个代码库中显式地控制它

一种非常有效的方法是将您的服务层放在一个通用操作后面。虽然这个抽象的名称并不相关,但我通常称这个抽象为“命令处理程序:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}
这看起来可能只是一堆额外的代码,但有了这条消息和这一通用抽象,我们就可以轻松地应用横切关注点,例如处理工作单元:

public class CommitUnitOfWorkCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand> {

    private readonly IUnitOfWork unitOfWork;
    private readonly ICommandHandler<TCommand> decoratee;

    public CommitUnitOfWorkCommandHandlerDecorator(
        IUnitOfWork unitOfWork,
        ICommandHandler<TCommand> decoratee) {
        this.unitOfWork = unitOfWork;
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command) {
        this.decoratee.Handle(command);
        this.unitOfWork.SaveChanges();
    }
}
公共类CommitUnitOfWorkCommandHandlerDecorator
:ICommandHandler{
私有只读IUnitOfWork;
私有只读ICommandHandler装饰对象;
工作命令的公共委员会HandlerDecorator(
i工作单元工作单元,
ICommandHandler(受装饰者){
this.unitOfWork=unitOfWork;
this.decoree=decoree;
}
公共无效句柄(TCommand命令){
this.decoree.Handle(命令);
this.unitOfWork.SaveChanges();
}
}
上面的类是一个装饰器:它既实现了
ICommandHandler
,又包装了
ICommandHandler
。这允许您围绕每个命令处理程序实现包装此装饰器的实例,并允许系统透明地保存在工作单元中所做的更改,而无需任何代码明确地执行此操作

在这里也可以创建一个新的工作单元,但最简单的方法是让该工作单元在(web)请求期间有效

然而,这个decorator仅仅是你可以用decorator做的事情的开始。例如,它将是微不足道的:

  • 应用安全检查
  • 进行用户输入验证
  • 在事务中运行该操作
  • 应用死锁重试机制
  • 通过执行重复数据消除来防止重新存储
  • 在审核跟踪中注册每个操作
  • 存储用于排队或后台处理的命令

更多信息可以在文章中找到,以及。

相关:这与我的问题有什么关系?我想要一种在同一uow中重用服务的方法。调用其他服务的服务。每个服务也可以调用存储库。对不起,我应该添加更多的上下文。该答案描述了如何使用通用接口和装饰器删除工作单元的创建和管理,并改进整体应用程序设计。任何与上面示例匹配的有用代码都将不胜感激。感谢Steven将答案指向我的示例+1。如果命令中注入了许多依赖项,那么该命令内部又如何呢。就像是一项复杂的任务,需要对存储库或其他服务执行许多查询。如何保持它的简单性?一个命令可以调用另一个命令吗?@nerligma关于一个命令是什么有多种观点。在我的视图中,不应嵌套命令。关于如何保持简单,这是一个完整的讨论,但如果处理程序太大,应该从中提取较小的服务。
[Permission(Permissions.ManageCustomerDetails)]
public class UpdateCustomerDetailsCommand {
    public Guid CustomerId { get; set; }
    [Required] public string FirstName { get; set; }
    [Required] public string LastName { get; set; }
    [ValidBirthDate] public DateTime DateOfBirth { get; set; }
}

public class UpdateCustomerDetailsCommandHandler
    : ICommandHandler<UpdateCustomerDetailsCommand> {

    public UpdateCustomerDetailsCommandHandler(
        IRepository<Customer> _customerRepository, 
        IRepository<Order> orderRepository, 
        IManager itemManager) {
        _orderReporsitory = orderReporsitory;
        _itemManager = itemManager;
        _customerRepository = customerRepository;
    }

    public void Handle(UpdateCustomerDetailsCommand command) {
        var customer = _customerRepository.GetById(command.CustomerId);
        customer.FirstName = command.FirstName;
        customer.LastName = command.LastName;
        customer.DateOfBirth = command.DateOfBirth;
    }
}
public class CommitUnitOfWorkCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand> {

    private readonly IUnitOfWork unitOfWork;
    private readonly ICommandHandler<TCommand> decoratee;

    public CommitUnitOfWorkCommandHandlerDecorator(
        IUnitOfWork unitOfWork,
        ICommandHandler<TCommand> decoratee) {
        this.unitOfWork = unitOfWork;
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command) {
        this.decoratee.Handle(command);
        this.unitOfWork.SaveChanges();
    }
}