我有一个使用WebApi,Generic Repository,EF6和工作单元模式的场景 (为了将几次调用的所有更改都包装到同一个上下文中。)
Manager层用于执行对不同存储库以及其他管理器的调用。
目前,客户经理确实注入了回购和其他经理,如:
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);
}
}
此代码无法编译,仅供参考。
像这样的方法
将多个存储库包装在一个工作单元下并一起刷新所有更改。
我的问题还涉及添加另一个Manager层,也包含在工作单元内,允许同时调用存储库和其他管理器 (因为我想重用一些管理器逻辑。就像在示例中,我正在重用一些ItemManager逻辑)
此代码https://stackoverflow.com/a/15527444/310107
using (var uow = new UnitOfWork<CompanyContext>())
{
var catService = new Services.CategoryService(uow);
var custService = new Services.CustomerService(uow);
var cat = new Model.Category { Name = catName };
catService.Add(dep);
custService.Add(new Model.Customer { Name = custName, Category = cat });
uow.Save();
}
正在使用类似我需要的东西,但我也希望能够将服务注入单元测试它们(而不是在我的经理/服务方法体中创建实例)
这样做的最佳方法是什么?
由于
答案 0 :(得分:2)
您的工作单元代码段有几个问题,例如:
CategoryService
和CustomerService
),这会使您的代码变得复杂并使您的代码更难以测试。我在this answer中更详细地表达了这些问题。
相反,我建议有一个DbContext
,通过完整的请求共享它,并在应用程序的基础结构中控制它的生命周期,而不是在整个代码库中显式控制它。
执行此操作的一种非常有效的方法是将服务层置于通用抽象之后。虽然这个抽象的名称是无关紧要的,但我通常称之为抽象'命令处理程序:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
这个抽象有一些有趣的事情:
例如,您的CustomerManager
可能如下所示:
[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();
}
}
上面的类是一个装饰器:它都实现ICommandHandler<TCommand>
并包装ICommandHandler<TCommand>
。这允许您围绕每个命令处理程序实现包装此装饰器的实例,并允许系统透明地保存在工作单元中所做的更改,而无需任何代码必须明确地执行此操作。
也可以在这里创建一个新的工作单元,但最简单的方法是让工作单元在(web)请求期间保持有效。
然而,这个装饰器只是你可以用装饰器做的开始。例如,它将是微不足道的: