【发布时间】:2019-07-15 15:37:56
【问题描述】:
N 层设计,我有 UserRepository 负责数据库的工作,UserManager 负责业务逻辑,还有一个控制器。
我为订单发送OrderManager 和OrderRepository。
在我的UserManager中,有DoUserStuffThenAddAnOrder()之类的方法
这个方法会做一些与用户相关的事情,然后在我的OrderManager 上调用AddOrder()。
所以我将OrderManager 依赖注入到我的UserManager 中,以便它可以调用与订单相关的方法。
但是我在OrderManager 遇到了同样的问题,我想DoOrderStuffThenChangeAUser(),所以我将UserManager 输入OrderManager。
然后我的 DI 因为循环引用而中断;创建一个 OrderManager 需要将一个 UserManager 注入其中,注入一个 usermanager 需要一个 ordermanager,等等等等。
有解决办法吗?也许是一种告诉它在 DI 设置中将自己作为参数传入的方法?
作为一种解决方法,我创建了一个OrderManagerFactory 和一个UserManagerFactory,并在每个上创建了两个重载的构造函数,例如,创建新实例的控制器将 DI 一个 OrderManagerFactory,它在构造函数中将调用 .GetOrderManager(this)重载的构造函数,反之亦然。
但是有两个构造函数不是很好,如果我创建更多的管理器,那么它可能会创建一个注入和构造函数的地狱。
有没有一个巧妙的解决方案?其他问题表明,如果您遇到此问题,您的软件架构不正确,但 SRP 会要求每个经理做自己的事情并允许他们互相调用以使业务逻辑独立,这似乎是合理的。
这是一个例子:
public class UserManager {
private IOrderManager _orderManager;
public UserManager(IOrderManager orderManager)
{
_orderManager = orderManager;
}
public void DeleteUser(int userId) {
if (_orderManager.GetOrdersForUser(userId).Count != 0) {
throw new Exception("Cannot delete user while orders exist");
}
// Do a bunch of stuff connected with deleting a user
// Send them a goodbye e-mail
EmailUser(123, "Goodbye");
}
public void EmailUser(int userId, string message)
{
var userMail = _repository.GetUser(userId).EmailAddress;
_emailSomething.Send(userMail, message);
}
}
和
public class OrderManager {
private IUserManager _userManager;
public OrderManager(IUserManager userManager)
{
_userManager = userManager;
}
public IEnumerable<OrderDto> GetOrdersForUser(int userId)
{
return Map(_repo.GetOrdersForUserId(userId));
}
public void CancelOrder(int orderId)
{
var userId = _repo.GetOrder(orderId).UserId;
// Cancel order
var user = _userManager.GetUserById(userId);
_userManager.EmailUser(userId, "Hello " + user.Name + ", your order has been cancelled");
}
}
这是不可能的,因为 IOrderManager 和 IUserManager 都不能在没有循环引用错误的情况下注入。
【问题讨论】:
-
如果不使用中间抽象(例如工厂、
Lazy等),就不可能将这些对象new起来 -
你认为基础设计有问题吗?一种选择是代替
UserManager>OrderManager>OrderRepository来简单地注入存储库,然后执行UserManager>OrderRepository。但是这样做的风险是你会削减或不得不复制一些业务逻辑。 -
设计似乎比它需要的更复杂,而且 IMO 有点重复比管理循环依赖更好。
-
循环依赖通常是由违反单一责任原则引起的。将您的课程分成更小、更集中的课程可能会很好地解决您的问题。
-
我不知道你如何进一步拆分它,如果
UserManager有一个SendEmailToUser()方法,你可能想在订单完成时在你的OrderManager中调用它,同样如果用户被删除,您可能需要在您的OrderManager上调用CancelAllOrders()。我看不出你可以如何简化它,这样他们就不需要在没有一点点麻烦的情况下互相交谈。
标签: dependency-injection asp.net-core-2.0 circular-dependency