服务层应该为MVC应用程序返回视图模型吗?
假设你有一个ASP.NET MVC项目并且正在使用一个服务层,比如在asp.net网站的这个联系人pipe理器教程中: http : //www.asp.net/mvc/tutorials/iteration-4-make-的应用程序,松散耦合-CS
如果您的视图有视图模型,服务层是否提供每个视图模型的适当位置? 例如,在服务层代码示例中有一个方法
public IEnumerable<Contact> ListContacts() { return _repository.ListContacts(); }
如果你想要一个IEnumerable,它应该在服务层,还是有其他地方是“正确”的地方?
也许更恰当的是,如果你为每个与ContactController关联的视图有一个单独的视图模型,那么ContactManagerService应该有一个单独的方法来返回每个视图模型? 如果服务层不适合,控制器应该在哪里初始化viewmodel对象?
一般来说,不。
视图模型旨在向视图提供信息,并且应该是特定于应用程序的,而不是一般的域。 控制器应该协调与存储库,服务(我在这里对服务的定义进行一些假设)等的交互,并处理构build和validation视图模型,还包含确定视图的逻辑。
通过将视图模型泄露到“服务”层中,您正在模糊您的图层,现在可能将具体的应用程序和演示文稿混合在与域级职责相关的内容中。
不,我不这么认为。 服务应该只关心问题域,而不关心呈现结果的视图。 返回值应该用域对象来表示,而不是视图。
按照传统的方法或理论上,ViewModel应该是用户界面层的一部分。 至less这个名字是这么说的。
但是当你真正用Entity Framework,MVC,Repository等来实现它的时候,你会意识到别的东西。
有人必须将实体/数据库模型与ViewModel(最后提到的DTO)进行映射。 这应该在[A] UI层(由控制器)还是在[B]服务层完成?
我select了选项B.选项A是一个不是没有,因为几个实体模型组合在一起形成一个ViewModel这个简单的事实。 我们可能不会将不必要的数据传递给UI层,而在选项B中,服务可以使用数据进行播放,并在映射到ViewModel之后将所需的/最小值传递到UI层。
再次,让我们selectA,把ViewModel放在UI层(和Service层的实体模型)。
如果Service层需要映射到ViewModel,那么Service层需要访问UI层的ViewModel。 哪个图书馆/项目? Viewmodel应该在UI层的一个独立的项目中,这个项目需要被Service Layer引用。 如果ViewModel不在一个单独的项目中,那么就有循环引用,所以不行。 有服务层访问UI层看起来很尴尬,但我们仍然可以应付它。
但是如果有另一个UI应用程序使用这个服务呢? 如果有移动应用程序呢? ViewModel有多不同? 服务应该访问相同的视图模型项目吗? 所有的UI项目都可以访问同一个ViewModel项目吗?或者他们有自己的项目吗?
经过这些考虑,我的答案是把Viewmodel项目放在服务层。 无论如何,每个UI层都必须访问服务层! 而且可能有很多类似的ViewModel,他们都可以使用(因此映射变得更容易服务层)。 现在映射是通过linq来完成的,这是另外一个好处。
最后是关于DTO的讨论。 还有关于ViewModels中的数据注释。 带有数据注释的ViewModel(Microsoft.Web.Mvc.DataAnnotations.dll)不能驻留在服务层,而是驻留在UI层(但ComponentModel.DataAnnotations.dll可以驻留在服务层中)。 所以DTO实际上是一个ViewModel,因为大多数情况下,两者之间会有一对一映射(比如用AutoMapper)。 DTO仍然具有用于UI(或多个应用程序)所需的逻辑并驻留在服务层中。 和UI层ViewModel(如果我们使用Microsoft.Web.Mvc.DataAnnotations.dll)只是从DTO复制数据,添加了一些“行为”/属性。
[现在这个讨论即将进行一个有趣的翻阅:…]
不要以为数据注释属性只是用于UI。 如果使用System.ComponentModel.DataAnnotations.dll限制validation,那么相同的viewmodel也可以用于前端和后端validation(从而删除UI驻留视图模型拷贝的DTO)。 实体模型也可以使用更多属性。 例如:使用.tt,Entity Framework数据模型可以使用validation属性自动生成,在发送到后端之前执行一些数据库validation,如最大长度。 另一个优点是,如果DB中的后端validation发生变化,那么.tt(读取数据库细节并为实体类创build属性)将自动select它。 这可以强制UIvalidationunit testing失败,这是一个很大的优势(所以我们可以纠正它,并通知所有用户界面/消费者,而不是意外忘记和失败)。 是的,讨论正在走向一个好的框架devise。 正如你所看到的,它是所有相关的:分层validation,unit testing策略,caching策略等。
虽然没有直接关系到这个问题。 这里提到的“ViewModelFaçade”必须观看频道9的链接也值得探讨。 它在video中以11分49秒开始。 因为一旦你上面给出的问题被整理出来,这将是下一步/思考:“如何组织视图模型?
同样在你的例子“_repository.ListContacts()”正在从存储库返回一个视图模型。 这不是一个成熟的方法。 存储库应该提供实体模型或db模型。 这被转换为视图模型,这是服务层返回的这个视图模型。
我想这取决于你认为的“服务”是什么。 我从来没有真正喜欢过单一课堂的服务这个术语; 这是非常含糊的,并没有告诉你这个class的实际目的。
如果“服务层”是一个物理层,比如web服务,那么绝对不是; SOA环境中的服务应公开域/业务操作,而不是数据,而不是表示逻辑。 但是如果服务只是作为一个抽象的概念来进一步封装,我不认为你用这种方式使用它是有问题的。
只是不要混合概念。 如果您的服务处理视图模型,那么它应该是一个演示服务,并且应该在实际模型的顶层进行分层,而不是直接触摸数据库或任何业务逻辑。
这有点“取决于”我在哪里工作 – 我们通常有一个控制器消耗一些服务 – 然后将返回的DTO合并成一个“ViewModel”,然后传递给客户端 – 通过JSON结果,或绑定在剃刀模板上。
事情是,大约80%的时间 – DTO到ViewModel的映射已经是1-1。 我们正在开始转向“需要的地方,直接使用DTO,但是当DTO和我们客户端/视图中需要的东西不匹配时 – 那么我们创build一个ViewModel并根据需要在对象之间进行映射。
尽pipe我仍然不相信这是最好的或者正确的解决scheme,但是最终导致了一些激烈的讨论:“我们只是把X添加到DTO来满足视图的需求?