65

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

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

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

相反,如果您想要一个 IEnumerable,它应该放在服务层中,还是有其他地方是“正确”的地方?

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

4

6 回答 6

52

一般来说,没有。

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

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

于 2010-06-09T00:18:43.840 回答
27

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

于 2010-06-09T00:07:07.120 回答
26

As per the traditional approach or theory wise, ViewModel should be part of User interface layer. At least the name says so.

But when you get down to implementing it yourself with Entity Framework, MVC, Repository etc, then you realise something else.

Someone has to map Entity/DB Models with ViewModels(DTO mentioned in the end). Should this be done in [A] the UI layer (by the Controller), or in [B] the Service layer?

I go with Option B. Option A is a no no because of the simple fact that several entity models combine together to form a ViewModel. We may not pass unnecessary data to UI layer, whereas in option B, the service can play with data and pass only the required/minimum to the UI layer after mapping (to the ViewModel).

But still, let us go with option A, put ViewModel in the UI layer(and entity model in Service layer).

If the Service layer needs to map to the ViewModel, then the Service layer need to access ViewModel in UI layer. Which library/project? The Viewmodel should be in a separate project in the UI layer, and this project needs to be referenced by Service Layer. If the ViewModel is not in a separate project, then there is circular reference, so no go. It looks awkward to have Service layer accessing UI layer but still we could cope with it.

But what if there is another UI app using this service? What if there is a mobile app? How different can the ViewModel be? Should the Service access the same view model project? Will all UI projects access the same ViewModel project or they have their own?

After these considerations my answer would be to put the Viewmodel project in Service Layer. Every UI layer has to access the Service layer anyways! And there could be a lot of similar ViewModels that they all could use (hence mapping becomes easier for service layer). Mappings are done through linq these days, which is another plus.

Lastly, there is this discussion about DTO. And also about data annotation in ViewModels. ViewModels with data annotations(Microsoft.Web.Mvc.DataAnnotations.dll) cannot reside in service layer instead they reside in UI layer(but ComponentModel.DataAnnotations.dll can reside in service layer). If all projects are in one solution(.sln), then it doesn't matter which layer you put it. In enterprise applications, each layer will have its own solution.

So DTO actually is a ViewModel because mostly there will be one on one mapping between the two(say with AutoMapper). Again DTO still has the logic needed for the UI(or multiple applications) and resides in Service Layer. And the UI layer ViewModel(if we use Microsoft.Web.Mvc.DataAnnotations.dll) is just to copy the data from DTO, with some 'behavior'/attributes added to it.

[Now this discussion is about to take an interesting turn read on...:I]

And don't think data-annotation attributes are just for UI. If you limit the validation using System.ComponentModel.DataAnnotations.dll then the same ViewModel can also be used for front-end & backend validation(thus removing UI-residing-ViewModel-copy-of-DTO). Moreover attributes can also be used in entity models. Eg: using .tt, Entity Framework data models can be autogenerated with validation attributes to do some DB validations like max-length before sending to the back end. This saves round-trips from UI to backend for validation. It also enables back-end to re-validate for security reasons. So a model is a self-reliant validator and you can pass it around easily. Another advantage is that if backend validation changes in DB then .tt (reads DB specifics and create the attribute for entity class) will automatically pick that up. This can force UI validation unit tests to fail as well, which is a big plus(so we can correct it and inform all UIs/consumers instead of accidentally forgetting and failing). Yes, the discussion is moving towards a good framework design. As you can see it is all related: tier-wise validation, unit test strategy, caching strategy, etc.

Although not directly related to the question. 'ViewModel Façade' mentioned in this must watch channel 9 link is also worth exploring. It starts exactly at 11 minutes 49 seconds in the video. Because this would be the next step/thought once your current question given above is sorted out: 'How to organize ViewModels?'

And Lastly, many of these model vs logic issues could be resolved with REST. Because every client can have the intelligence to query the data and get only the data that it needs. And it keeps the model in UI, there is no server/service layer model/logic. The only duplication then will be on the automated tests that each client need to perform. Also if there are changes in data then some clients fail if they do not adapt to the change. The question then is, are you removing service layer altogether(and the models they carry) or pushing the service layer up to your UI project(so model issue still persists) which calls the REST API. But in terms of the responsibility of Service layer, they are the same regardless.

Also in your example "_repository.ListContacts()" is returning a ViewModel from repository. This is not a mature way. Repositories should provide entity models or DB models. This gets converted to view models and it is this view model that is returned by the service layer.

于 2012-07-04T09:50:05.610 回答
6

这有点“取决于”我在哪里工作——我们通常有一个控制器使用一些服务——然后将返回的 DTO 组合成一个“ViewModel”,然后通过 JSON 结果传递给客户端,或绑定在 Razor 模板中。

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

尽管我仍然不相信这是最好或正确的解决方案——因为它最终导致了一些激烈的讨论:“我们只是将 X 添加到 DTO 以满足视图的需求吗?”

于 2013-07-25T14:42:53.343 回答
5

我想这取决于您认为“服务”是什么。我从来没有真正喜欢在单个课程的上下文中使用术语服务。它非常模糊,并没有告诉你太多关于课程的实际目的。

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

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

于 2010-06-08T23:49:26.873 回答
0

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

于 2020-11-10T08:14:49.930 回答