我和一些同事一起研究MVC应用程序。控制器都有一个使用Unity注入的ServiceFactory ...
public HomeController(IServiceFactory serviceFactory)
{
Services = serviceFactory
// Where Services is a property of the Controller
}
现在,我正在编写一些ViewModel,其中一些ViewModel需要从多个Services访问对象。所以,我有这样的模式...
public class MyViewModel
{
public MyViewModel(IServiceFactory services)
{
// Do stuff
}
}
我的控制器包含这样的代码......
public ActionResult SomeAction()
{
var model = new MyViewNodel(Services)
// ...
}
我的一些同事认为,这不是DI的正确使用,但似乎无法明确说明为什么会这样。
他们是对的吗?如果是这样的话?
答案 0 :(得分:4)
你的同事抛出危险信号的原因是因为你在注入服务时成功地练习了DI,但是你正在你的控制器动作中新建一个MyViewModel实例,这会隐藏"来自外部的实施细节。
现在,这就是说,因为这个控制器不应该直接来自其他代码,而是来自网络,所以你可以认为这是可以接受的。我个人的偏好是因为你已经开始注射依赖,所以现在不要停止!
修改强>
正如詹姆斯在评论中提到的那样,通过一个独立的注入工厂解决你的viewmodel实例会好得多。这样您就可以在同一个控制器类中解析多个视图。
private readonly IViewModelFactory _factory;
public HomeController(IViewModelFactory factory)
{
_factory = factory;
var model = _factory.GetViewModelInstance();
// Where Services is a property of the Controller
}
注册时
IMyViewModelFactory
到您的具体类,解决容器中IServiceFactory的注入,将其突出显示为注入依赖项。
public ViewModelFactory(IServiceFactory factory)
答案 1 :(得分:3)
看起来对我来说。 ViewModels
依赖于某些服务。因此,不是在ViewModel
内实例化那些服务,而是通过构造函数注入。一个非常简单的DI案例。
答案 2 :(得分:0)
这取决于您对设计的看法,但您的应用程序中出现的问题IMO是您将数据与行为混合在一起。您应该提供视图的视图模型应该是DTO;纯数据对象。您不希望这些对象具有任何行为,因为这会隐藏应用程序中的逻辑。视图呈现时执行的所有逻辑应尽可能简单。这不仅使得应用程序代码的推理变得更简单,而且还会阻止您为视图编写自动化测试(这是一种绝对的痛苦)。
当您将视图模型用作“后置模型”时(非常常见且方便),让视图模型成为除简单DTO之外的其他任何内容都会相当快速地分解。 MVC无法建模绑定您的复杂视图模型,除非您编写一个自定义绑定器,将这些依赖项注入其中;或至少告诉MVC如何构建该类型。这非常不实用,并为每个视图模型带来了大量额外代码。虽然可以通过定义单独的“仅后期”视图模型来解决此问题,但这又会导致重复的代码。这两个模型会很快失去同步(我从中获得经验)。
由于您在视图模型中混合了行为,因此您必须在控制器中手动执行依赖项注入(就像现在一样),或者需要将此代码提取到工厂。两者都有其缺点。
像现在一样注入这些依赖项,用注入代码污染控制器;它不应该被关注的东西。构造函数应该是一个实现细节(只有类本身和组合根关注),但现在每次视图模型的构造函数发生更改时,此更改都会冒泡到控制器。
将其解压缩到工厂可能会解决该问题,但会强制您为每个控制器编写工厂。虽然有些容器包含为您自动生成这些工厂的功能,但您的代码中仍然有这种额外的抽象;如果你保持你的视图模型是一个不起眼的DTO,那就不需要抽象了。
所以一般来说你应该遵循这条规则:
创建无状态服务的对象图并尽早构建它们。之后,通过图表传递纯数据。
因此,在您的情况下,视图模型是数据。数据不应取决于服务;服务应该依赖于数据。您让您的服务(您的控制器)创建该数据(视图模型)并将其传递(将其返回到视图)。