从这个问题看,让控制器创建一个更准确地反映视图试图显示的模型的ViewModel似乎是有道理的,但我对一些约定感到好奇(我是MVC模式的新手) ,如果还不是很明显的话).
基本上,我有以下问题:
我通常喜欢有一个类/文件.如果仅创建将数据从控制器传递到视图,那么这对ViewModel有意义吗?
如果ViewModel确实属于它自己的文件,并且您正在使用目录/项目结构来保持独立,那么ViewModel文件属于哪里?在Controllers目录中?
这基本上就是现在.我可能会再提出一些问题,但是在最后一个小时左右这一直困扰着我,我似乎在其他地方找到了一致的指导.
编辑: 看看CodePlex 上的示例NerdDinner应用程序,它看起来像ViewModel是控制器的一部分,但它仍然让我感到不舒服,他们不在他们自己的文件中.
我为每个视图创建了我称之为"ViewModel"的东西.我将它们放在我的MVC Web项目中名为ViewModels的文件夹中.我在它们代表的控制器和动作(或视图)之后命名它们.因此,如果我需要将数据传递到Membership控制器上的SignUp视图,我创建一个MembershipSignUpViewModel.cs类并将其放在ViewModels文件夹中.
然后我添加必要的属性和方法,以方便从控制器到视图的数据传输.我使用Automapper从我的ViewModel到域模型,然后在必要时再次返回.
这也适用于包含其他ViewModel类型属性的复合ViewModel.例如,如果在成员资格控制器的索引页面上有5个小部件,并且您为每个部分视图创建了一个ViewModel - 如何将数据从Index操作传递给partials?您将属性添加到MyPartialViewModel类型的MembershipIndexViewModel,并且在渲染部分时,您将传递Model.MyPartialViewModel.
这样做可以让您调整部分ViewModel属性,而无需更改索引视图.它仍然只是在Model.MyPartialViewModel中传递,因此当您所做的只是向部分ViewModel添加属性时,您将不太可能通过整个局部链来修复某些东西.
我还将命名空间"MyProject.Web.ViewModels"添加到web.config中,以便允许我在任何视图中引用它们,而无需在每个视图上添加显式的import语句.只是让它更清洁一点.
按类别分隔类(控制器,ViewModel,过滤器等)是无稽之谈.
如果要为网站的Home部分编写代码(/),则创建一个名为Home的文件夹,并将HomeController,IndexViewModel,AboutViewModel等以及Home操作使用的所有相关类放在那里.
如果您有共享类,比如ApplicationController,您可以将它放在项目的根目录下.
为什么要将相关的东西(HomeController,IndexViewModel)分开并将所有东西放在一起(HomeController,AccountController)?
我写了一篇关于这个主题的博客文章.
我将我的应用程序类保存在名为"Core"(或单独的类库)的子文件夹中,并使用与KIGG示例应用程序相同的方法,但稍微进行一些更改以使我的应用程序更加干燥.
我在/ Core/ViewData /中创建了一个BaseViewData类,我存储了常见的站点范围属性.
在此之后,我还在同一文件夹中创建我的所有视图ViewData类,然后从BaseViewData派生并具有视图特定属性.
然后我创建一个我的所有控制器派生自的ApplicationController.ApplicationController有一个通用的GetViewData方法,如下所示:
protected T GetViewData() where T : BaseViewData, new() { var viewData = new T { Property1 = "value1", Property2 = this.Method() // in the ApplicationController }; return viewData; }
最后,在我的Controller操作中,我执行以下操作来构建我的ViewData模型
public ActionResult Index(int? id) { var viewData = this.GetViewData(); viewData.Page = this.DataContext.getPage(id); // ApplicationController ViewData.Model = viewData; return View(); }
我认为这很有效,它可以保持你的视野整洁,你的控制器很瘦.
ViewModel类用于将由类实例表示的多个数据封装到一个易于管理的对象中,您可以将其传递给View.
将ViewModel类放在自己的文件中,在自己的目录中是有意义的.在我的项目中,我有一个名为ViewModels的Models文件夹的子文件夹.这就是我的ViewModels(例如ProductViewModel.cs
)所在的地方.
没有好的地方可以保留你的模型.如果项目很大并且有很多ViewModel(数据传输对象),你可以将它们保存在单独的程序集中.您也可以将它们保存在站点项目的单独文件夹中.例如,在Oxite中,它们被放置在Oxite项目中,该项目也包含许多不同的类.Oxite中的控制器被移动到单独的项目中,视图也在单独的项目中.
在CodeCampServer中, ViewModel被命名为*Form,它们被放置在Models文件夹中的UI项目中.
在MvcPress项目中,它们被放置在Data项目中,该项目还包含与数据库一起使用的所有代码以及更多(但我不推荐这种方法,它仅用于示例)
所以你可以看到有很多观点.我通常将我的ViewModel(DTO对象)保存在站点项目中.但是,当我有超过10个模型时,我更愿意将它们移动到单独的组件中.通常在这种情况下我也将控制器移动到单独的组件中.
另一个问题是如何轻松地将模型中的所有数据映射到ViewModel.我建议看一下AutoMapper库.我非常喜欢它,它为我做了所有肮脏的工作.
我还建议你看一下SharpArchitecture项目.它为项目提供了非常好的架构,它包含许多很酷的框架和指南以及很棒的社区.
这是我最佳实践的代码片段:
public class UserController : Controller { private readonly IUserService userService; private readonly IBuildercreateBuilder; private readonly IBuilder editBuilder; public UserController(IUserService userService, IBuilder createBuilder, IBuilder editBuilder) { this.userService = userService; this.editBuilder = editBuilder; this.createBuilder = createBuilder; } public ActionResult Index(int? page) { return View(userService.GetPage(page ?? 1, 5)); } public ActionResult Create() { return View(createBuilder.BuildInput(new User())); } [HttpPost] public ActionResult Create(UserCreateInput input) { if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol"); if (!ModelState.IsValid) return View(createBuilder.RebuildInput(input)); userService.Create(createBuilder.BuilEntity(input)); return RedirectToAction("Index"); } public ActionResult Edit(long id) { return View(editBuilder.BuildInput(userService.GetFull(id))); } [HttpPost] public ActionResult Edit(UserEditInput input) { if (!ModelState.IsValid) return View(editBuilder.RebuildInput(input)); userService.Save(editBuilder.BuilEntity(input)); return RedirectToAction("Index"); } }
我们将所有ViewModel抛出到Models文件夹中(我们所有的业务逻辑都在一个单独的ServiceLayer项目中)