MVC 3 - 控制器和视图模型 - 哪个应该包含大部分业务逻辑?
Posted
技术标签:
【中文标题】MVC 3 - 控制器和视图模型 - 哪个应该包含大部分业务逻辑?【英文标题】:MVC 3 - Controllers and ViewModels - Which should contain most of the business logic? 【发布时间】:2011-11-15 12:21:57 【问题描述】:目前在我的应用程序中并使用工作单元模式和通用存储库,我的所有控制器都包含所有业务逻辑。我正在使用 ViewModels 而不是直接模型。
虽然这是一个好主意,但现在出现了一个问题,它可以显着分离控制器中的业务逻辑。对于控制器和 ViewModel,应该包含大部分业务逻辑?
我尝试了几种方法来让我的 ViewModel 实际上包含所有业务逻辑。但是,我的 ViewModel 的构造函数中必须有一个参数,该参数需要一个工作单元。这是个好主意吗?
我的代码气味告诉我它是。但是,我只是有点担心这将如何与执行不需要 ViewModel 的操作的控制器保持一致。简单地说,不需要将 Model/ViewModel 传递给 View 的操作;这种情况发生在重定向到其他操作的操作上。这意味着,我的业务逻辑可以保留在该操作中,也可以将该业务逻辑分离到一个函数中。
这里的最佳做法是什么?
【问题讨论】:
【参考方案1】:我不能说我的方法是最佳实践,但我更喜欢将任何业务逻辑放在单独的“服务”层中。
我倾向于使用 ViewModel 仅存储特定视图所需的属性。如果 ViewModel 上有任何方法,它们很可能只是检索与该视图相关的集合。
我尽量将控制器限制为验证和重定向/显示视图,从而使控制器保持轻量级。
因此,如果我有任何复杂的逻辑,我将调用一个单独的服务来调用控制器操作来处理该逻辑。这样逻辑就被隔离了,这使得测试更容易,因为不再需要创建控制器或 ViewModel 来测试它。重用服务也比拆分 ViewModel 更容易。
希望这会有所帮助。祝你好运。
【讨论】:
+1 - 这似乎是一种合理的方法;但是,我的问题仍然没有得到回答。我知道 ViewModel 的用途,我正在尝试弄清楚 ViewModel 是否应该包含尽可能多的业务逻辑。 @TIHan,他说 ViewModel 只包含数据。服务执行该过程。控制器进行流量控制。 ViewModel 应该只是一个poco,其中包含视图的所有最终数据。你如何获取数据、处理数据、格式化数据等等……直到服务层。 @rockinthesixstring,感谢您为我简化了这一点。听起来是个好计划;但是,我必须弄清楚我是否真的需要做这样的事情。 最终的结果是你会有更多的类,但如果你做得正确,它们应该更容易阅读、测试和维护。我已经好几次想对我的 ViewModel 太花哨了。这是一篇很好的文章,详细介绍了这种方法(从控制器端):devproconnections.com/article/aspnetmvc/…【参考方案2】:对于控制器和视图模型,应该包含大部分业务逻辑?
没有。
我尝试了几种方法来让我的 ViewModel 实际上包含所有业务逻辑。但是,我的 ViewModel 的构造函数中必须有一个参数,该参数需要一个工作单元。这是个好主意吗?
恕我直言,这是一个非常糟糕的主意。首先,您违反了一些 SOLID 原则。将所有代码捆绑到视图模型中会使测试变得困难。如果你想在另一个视图中使用一些业务逻辑怎么办?你复制那个代码吗?
这里的最佳做法是什么?
让我们先回到 MVC 模式。这是一个相当宽泛的定义,但知道它应该让您感觉应该在哪里放置什么。
MVC 中的“模型”实际上是用于将数据拉到一起的所有内容。它可以是网络服务、业务层、存储库等。
视图是生成 html 的所有代码(因为我们谈论的是 Web)。
控制器应该被认为是模型和视图之间的粘合剂。因此,它应该从模型中获取信息并将其转换为视图可用的东西。
这种结构的问题在于,很容易将特定层的信息“泄漏”到模式的其他部分中。因此,Microsoft 将 ViewModels 引入到他们的 MVC 实现中。
通过这种方式,我们可以从视图中移除所有渲染逻辑并将其放入 ViewModel。而不是在你看来这样做:
<span>@(model.Age == 0 ? "n/a" : model.Age)</span>
您将代码放在 ViewModel 中,然后直接调用 @model.Age
。通过这种方式,您不必在使用您的视图模型的所有视图中复制该代码。
关于 ViewModel 的问题的答案是,它应该只包含用于正确呈现来自“模型”的信息的逻辑。
至于控制器,我也不会将任何业务逻辑放入其中。首先,它使测试您的逻辑变得非常困难。然后你给它增加更多的责任(这样做会破坏 SRP)。在控制器中唯一有效的逻辑是从 ViewModel 中获取信息并将其转换为“模型”可用的东西,反之亦然。
希望能回答你的问题。
更新
我会创建一个单独的项目并向其中添加类。然后只需从您的 web 项目中添加一个引用并在控制器中调用这些类。
我还将开始使用控制容器的反转来自动为我创建这些依赖项。
Autofac 既可以为您发现服务(零配置),也可以将自己注入 MVC。
要遵循分离的界面模式,请创建以下项目:
YourProject.BusinessLayer YourProject.BusinessLayer.Specification YourProject.Mvc“规范”项目可用于更容易测试事物并更容易切换实现(可能只有几个类,不一定是整个业务层)。阅读“分离接口模式”
【讨论】:
【参考方案3】:模型-视图-视图模型 (MVVM) 是一种用于构建用户界面的设计模式。您的视图模型是 UI 上数据和操作的纯代码表示。因此,它应该包含与该 UI 相关的逻辑。
例如:
如果您正在实现列表编辑器,您的视图模型将是一个包含项目列表的对象,并公开添加和删除项目的方法。
来自***:
ViewModel:ViewModel 是“视图的模型”,意味着它是 视图的抽象,也用于数据绑定 视图和模型。它可以被看作是什么的一个特殊方面 将是充当数据的控制器(在 MVC 模式中) 将模型信息更改为视图信息的活页夹/转换器 并将视图中的命令传递给模型。视图模型 公开公共属性、命令和抽象。视图模型 被比作数据的概念状态,而不是 模型中数据的真实状态。[7]
【讨论】:
这一切都很好,但我想知道这如何与通用存储库和工作单元模式一起使用。【参考方案4】:您的控制器调用 UoW 来获取构建视图模型所需的数据。 你可以调用你的 UoW 的多个方法
然后您将所有需要的数据传递给您的视图模型构造函数。 (将 Uow 传递给 de viewmodel 听起来真的很糟糕)
如果您的控制器需要一些复杂的“逻辑”,从 UoW 调用许多方法等,您应该考虑创建另一个存储库或仅业务逻辑层来完成所有艰苦的工作,然后从控制器调用它,例如
SomeClass something = Uow.BLGoodName.DoSomeFancyStuff(params ..)
ViewData.model = new ControllerActionViewModel(something);
Return View();
【讨论】:
以上是关于MVC 3 - 控制器和视图模型 - 哪个应该包含大部分业务逻辑?的主要内容,如果未能解决你的问题,请参考以下文章