我的 Mapper class 应该注入我的控制器还是我可以直接实例化它?

Should my Mapper class be injected into my controller or can I instantiate it directly?

在我的控制器中,所有依赖项都是通过注入接收的,遵循 Dependency inversion principle,除了一个,Mapper class,它由构造函数实例化:

public class HomeController : Controller
{
    private readonly ISomeAppService SomeAppService;
    private readonly Mapper Mapper;

    public HomeController(ISomeAppService someAppService)
    {
        SomeAppService = someAppService;
        Mapper = new Mapper();
    }

    public ActionResult Index()
    {
        var someList = SomeAppService.GetSomeList();
        var someListDTO = Mapper.Map(someList);
        return View(new HomeIndexViewModel(someListDTO));
    }

SomeAppService 是我的域层前面的应用层服务。 Mapper 接收域对象和 returns DTO 以供视图使用。

我的理由是,自 ViewModel represents only the data that you want to display on your view/page 以来,我无法预见任何需要用其他东西替换 Mapper 的情况,或者这对测试有何不利。我也看不到这个 Mapper class 在任何其他表示层上被重用,因为其他视图可能与 Web 表示不同。对我来说,它感觉像是控制器的一部分。

问题是,这是正确的吗?我需要通过依赖注入接收映射器吗?我需要为它定义一个接口吗?如果是这样,原因是什么?我想遵守 SOLID 原则,但我想知道它们是否以及如何适用于此。

我个人建议注入您的 Mapper 实例。这正是 IoC 容器旨在管理的内容。

它没有理由在您的控制器中实例化,并且在其当前状态下违反了开闭原则和 SOLID 原则的控制反转。

使用 IoC 容器注入它的好处是:

改进的可测试性

通过将 Mapper 实例注入控制器,您将能够为其创建模拟以编写更好的测试。虽然您现在可以测试您的控制器,但您无法在任何条件下测试控制器内的映射器实例。

改进的可扩展性

几个月后,当您希望能够将构造函数参数传递到您的映射器时会发生什么?您需要完成所有控制器操作并更新构造函数。通过将创建 Mapper 实例的责任传递给您的 IoC 容器,您正在创建一个配置点,这意味着对您的映射器的任何进一步修改或更改 class 都可以在一个地方进行管理和配置。

对于大多数预计会使用超过几个月的软件,您可以确定一件事 - 它会发生变化。虽然您现在可能看不到更改映射器实例的原因,但以一种使您能够尽可能轻松地进行更改的方式设计软件是一种很好的做法。您必须更改的点点滴滴越多,您破坏某些东西或引入错误的可能性就越大。

除了@JoeMighty 上面给出的所有好处,我还要补充一点,通过在控制器中注入 Mapper,您可以外部化 Mapper.Map 定义,从而释放控制器的责任。此外,您可以通过控制注入的 Mapper 的生命周期来缓存映射定义,因此 Mapper 不必在每个请求上都创建定义。