服务层应该返回MVC应用程序的视图模型吗?

时间:2010-06-08 23:45:03

标签: asp.net-mvc service-layer asp.net-mvc-viewmodel

假设您有一个ASP.NET MVC项目并且正在使用服务层,例如在asp.net站点上的此联系人管理器教程中:http://www.asp.net/mvc/tutorials/iteration-4-make-the-application-loosely-coupled-cs

如果您有视图的视图模型,那么服务层是否适合提供每个视图模型?例如,在服务层代码示例中有一个方法

    public IEnumerable<Contact> ListContacts()
    {
        return _repository.ListContacts();
    }

如果您想要一个IEnumerable,它应该进入服务层,还是其他地方是“正确”的地方?

也许更合适的是,如果您为与ContactController关联的每个视图都有一个单独的viewmodel,那么ContactManagerService是否应该有一个单独的方法来返回每个viewmodel?如果服务层不是正确的位置,那么viewmodel对象应该在哪里被初始化以供控制器使用?

6 个答案:

答案 0 :(得分:46)

一般来说,没有。

视图模型旨在向视图提供信息,并且应该特定于应用程序,而不是通用域。控制器应该协调与存储库,服务的交互(我在这里做一些服务定义的假设)等,并处理构建和验证视图模型,还包含确定要呈现的视图的逻辑。

通过将视图模型泄漏到“服务”层,您可以模糊您的图层,现在可以将特定的应用程序和演示文稿与应该关注域级职责的内容混合在一起。

答案 1 :(得分:24)

不,我不这么认为。服务应该只关心问题域,而不是关注呈现结果的视图。返回值应以域对象而非视图表示。

答案 2 :(得分:21)

根据传统方法或理论,ViewModel应该是User界面层的一部分。至少这个名字是这样说的。

但是当你开始使用Entity Framework,MVC,Repository等实现它时,你就会意识到其他的东西。

有人必须使用ViewModels(最后提到的DTO)映射实体/数据库模型。这应该在[A] UI层(由Controller)或[B]服务层中完成吗?

我使用选项B.选项A是否定的,因为几个实体模型组合在一起形成ViewModel的简单事实。我们可能不会将不必要的数据传递到UI层,而在选项B中,服务可以使用数据并在映射(到ViewModel)之后仅将所需/最小值传递给UI层。

再次,让我们使用选项A,将ViewModel放在UI层(以及服务层中的实体模型)。

如果服务层需要映射到ViewModel,则服务层需要访问UI层中的ViewModel。哪个图书馆/项目? Viewmodel应位于UI层中的单独项目中,此项目需要由Service Layer引用。如果ViewModel不在一个单独的项目中,那么就有循环引用,所以没有去。让服务层访问UI层看起来很尴尬,但我们仍然可以应对它。

但是,如果有另一个使用此服务的UI应用程序呢?如果有移动应用程序怎么办? ViewModel有多么不同?服务应该访问相同的视图模型项目吗?是否所有UI项目都访问相同的ViewModel项目,或者他们有自己的项目?

在考虑这些因素后,我的回答是将Viewmodel项目放在服务层中。每个UI层都必须访问服务层!并且可能有许多类似的ViewModel都可以使用(因此服务层的映射变得更容易)。这些天通过linq完成映射,这是另一个好处。

最后,有关于DTO的讨论。还有关于ViewModels中的数据注释。带有数据注释的ViewModels(Microsoft.Web.Mvc.DataAnnotations.dll)不能驻留在服务层中,而是驻留在UI层中(但ComponentModel.DataAnnotations.dll可以驻留在服务层中)。如果所有项目都在一个解决方案(.sln)中,那么放置它的哪个层无关紧要。在企业应用程序中,每个层都有自己的解决方案。

所以DTO实际上是一个ViewModel,因为两者之间通常会有一对一的映射(比如AutoMapper)。 DTO仍然具有UI(或多个应用程序)所需的逻辑,并且驻留在服务层中。 UI层ViewModel(如果我们使用Microsoft.Web.Mvc.DataAnnotations.dll)只是从DTO复制数据,并添加了一些“行为”/属性。

[现在这个讨论即将进行一次有趣的转折......:I]

并且不要认为数据注释属性仅适用于UI。如果使用System.ComponentModel.DataAnnotations.dll限制验证 然后相同的ViewModel也可以用于前端和前端。后端验证(从而删除了UI-Residing-ViewModel-copy-of-DTO)。此外,属性也可用于实体模型。例如:使用.tt,可以使用验证属性自动生成实体框架数据模型,以便在发送到后端之前执行一些DB验证,例如max-length。另一个优点是,如果DB中的后端验证发生变化,那么.tt(读取DB特定内容并为实体类创建属性)将自动选择它。这可能会迫使UI验证单元测试失败,这是一个很大的优点(因此我们可以纠正它并通知所有UI /消费者,而不是意外忘记和失败)。是的,讨论正朝着良好的框架设计方向发展。正如您所看到的那样,它们都是相关的:分层验证,单元测试策略,缓存策略等。

虽然与问题没有直接关系。必须注意channel 9 link中提到的'ViewModelFaçade'也值得探索。它在视频中以11分49秒开始。因为一旦上面给出的当前问题得到解决,这将是下一步/思考:“如何组织ViewModels?”

同样在您的示例中,“_ _repository.ListContacts()”从存储库返回ViewModel。这不是一种成熟的方式。存储库应提供实体模型或数据库模型。这将转换为视图模型,这是服务层返回的视图模型。

答案 3 :(得分:5)

我认为这取决于你认为的“服务”。在单个类的上下文中,我从未真正喜欢 service 这个术语;这是非常模糊的,并没有告诉你很多关于课堂的实际目的。

如果“服务层”是物理层,例如Web服务,那么绝对不是; SOA上下文中的服务应该公开域/业务操作,而不是数据而不是表示逻辑。但是,如果将 service 用作进一步封装级别的抽象概念,我认为使用它的方式没有任何问题。

不要混淆概念。如果您的服务处理视图模型,那么它应该是表示服务并在实际模型之上分层,从不直接触及数据库或任何业务逻辑。

答案 4 :(得分:4)

这有点“依赖”我工作的地方 - 我们通常有一个控制器消耗一些服务 - 然后将返回的DTO组合成一个'ViewModel'然后传递给客户端 - 或者通过JSON结果,或绑定在Razor模板中。

事情是,大约80%的时间--DTO到ViewModel的映射是1-1。我们开始转向'在需要的地方,直接使用DTO,但是当DTO和我们在客户端/视图中需要的东西不匹配时 - 然后我们创建一个ViewModel并根据需要在对象之间进行映射'。

虽然我仍然不相信这是最好的或正确的解决方案 - 因为它最终引发了一些激烈的讨论'我们只是在DTO中添加X以满足观点的需求吗?'

答案 5 :(得分:0)

嗨,我在这里看到很好的答案。 对于我自己,我还有其他办法。 我必须使用各种模型,一种是视图模型,另一种是共享模型。视图模型位于UI层上,共享模型位于单独的项目中。 理论上,共享模型可用于任何软件,因为它们是独立的。 如果您要从服务层返回特定数据,或者需要从存储库中获取特定数据,则此模型提供一些抽象。 我真的不知道这是否是一个很好的方法,但是在我的项目中效果很好。例如 当我需要提供一些信息来为数据库创建新对象时,我可以将共享模型直接用于服务层,这为我节省了一些复杂性。 有时需要映射共享模型,但是您可以确保服务不会将原始数据泄漏到UI。 您可以将共享模型视为扩展,但不能使用共享模型来构建UI逻辑,因此应该具有视图模型。 您可以将视图模型与此共享模型结合使用,以节省使用继承的时间。 共享模型必须是中立的,并且不应具有任何逻辑。