当前位置:  开发笔记 > 编程语言 > 正文

ViewModel最佳实践

如何解决《ViewModel最佳实践》经验,为你挑选了7个好方法。

从这个问题看,让控制器创建一个更准确地反映视图试图显示的模型的ViewModel似乎是有道理的,但我对一些约定感到好奇(我是MVC模式的新手) ,如果还不是很明显的话).

基本上,我有以下问题:

    我通常喜欢有一个类/文件.如果仅创建将数据从控制器传递到视图,那么这对ViewModel有意义吗?

    如果ViewModel确实属于它自己的文件,并且您正在使用目录/项目结构来保持独立,那么ViewModel文件属于哪里?在Controllers目录中?

这基本上就是现在.我可能会再提出一些问题,但是在最后一个小时左右这一直困扰着我,我似乎在其他地方找到了一致的指导.

编辑: 看看CodePlex 上的示例NerdDinner应用程序,它看起来像ViewModel是控制器的一部分,但它仍然让我感到不舒服,他们不在他们自己的文件中.



1> Ryan Montgom..:

我为每个视图创建了我称之为"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语句.只是让它更清洁一点.


@Cosmo:然后POST到*action*,可以在模型错误的情况下返回整个视图.在服务器端,您有足够的资源来重新创建父模型.
如果要从局部视图POST并返回整个视图(如果出现模型错误),该怎么办?在部分视图中,您无权访问父模型.

2> Max Toro..:

按类别分隔类(控制器,ViewModel,过滤器等)是无稽之谈.

如果要为网站的Home部分编写代码(/),则创建一个名为Home的文件夹,并将HomeController,IndexViewModel,AboutViewModel等以及Home操作使用的所有相关类放在那里.

如果您有共享类,比如ApplicationController,您可以将它放在项目的根目录下.

为什么要将相关的东西(HomeController,IndexViewModel)分开并将所有东西放在一起(HomeController,AccountController)?


我写了一篇关于这个主题的博客文章.


@Max Toro:很惊讶你被投了很多.在ASP.Net MVC上工作一段时间后,我感到很痛苦,因为*所有*ViewModels都在一个地方,*所有*控制器在另一个地方,而*所有*视图在另一个地方.MVC是三个相关的部分,它们是*耦合的 - 它们相互支持.如果给定部分的Controller,ViewModel和Views一起存在于同一目录中,我觉得我的解决方案可以更加有条理.MyApp/Accounts/Controller.cs,MyApp/Accounts/Create/ViewModel.cs,MyApp/Accounts/Create/View.cshtml等.
不,麻烦的是将所有控制器放在一个目录/命名空间中.如果你有5个控制器,每个控制器使用5个视图模型,那么你有25个视图模型.命名空间是组织代码的机制,在这里不应该有任何不同.
如果你这样做,事情会很快变得相当混乱.
@RyanJMcGowan分离关注点不是类的分离.
@RyanJMcGowan我想补充Max的评论,并指出即使在最初的开发过程中我们也希望按功能工作.按功能划分的特征是例如SCRUM开发过程所采用的方法,其中每个故事都增加了业务价值.在花费2个月开发视图模型之后,有效添加的业务值仍为零,因为实际上没有任何东西可用.
@RyanJMcGowan无论你如何处理开发,问题都是你最终得到的,特别是对于大型应用程序.一旦进入维护模式,您不会考虑所有模型,而是所有控制器,您一次添加一个功能.
我相信在MVC2中添加的区域是试图提供这个问题的解决方案.您可以创建一个Area文件夹,其中包含自己的控制器,视图,模型,内容和脚本的子文件夹.连接它还需要做更多的工作,包括路由.但这在语义上分离问题是一种有用的方法.例如,所有网站管理员功能(创建用户,设置用户角色)都可以进入具有特殊权限的区域.或者,移动网站可以嵌套在区域中(在MVC4提出更好的方法之前,MVC3中使用的方法).
ASP.NET MVC不强制执行此分离.它们只是程序集上的类,它们可以位于任何目录/命名空间中.
@Max Toro:你也很惊讶.有一个php symfony框架,在1.x版本中它与RoR非常相似,但在symfony 2版本中,开发人员决定将所有内容分成像Max Toro建议的捆绑包,javascripts,样式表和图像.所有内容都包含在单独的包中,您可以非常轻松地在项目之间复制粘贴此包
@DOK你在区域内会遇到同样的问题.问题是控制器和ViewModel是分开的.
@jameszhao00谁对观点说了什么?
@jameszhao00你错了,控制器按类名搜索.实际上,在ControllerBuilder上有一个DefaultNamespaces集合,当2个控制器具有相同的类名但命名空间不同时,可以使用它来消除歧义.
我明白那个.我只是在接近MVC开发时间表时区分两种方法.如果按照所有模型的顺序开发项目,那么所有控制器,然后是所有视图,MVC文件夹结构也是有意义的.如果您同时在模型,视图和控制器方面接近项目,并从一个类别转到另一个类别,那么您的文件夹结构反映出更有意义.就个人而言,我发现一次做一件*类型*的东西比工厂工人更有效率.
猜猜我并不感到惊讶,每个人都认为你错了,因为项目模板没有那样运行......我将在我的下一个MVC项目中尝试这种方法.给予好评!是的,它绝对是Rails的结转.
只是为了搭载,这跟随(先于?)Google自己的Angular(MVVM)应用程序结构的最佳实践.这是我想要遵循的风格,我只是想找到用于将HomeController.cs与Index.cshtml相关联的逻辑.https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub

3> Mark..:

我将我的应用程序类保存在名为"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();
    }

我认为这很有效,它可以保持你的视野整洁,你的控制器很瘦.



4> JMS..:

ViewModel类用于将由类实例表示的多个数据封装到一个易于管理的对象中,您可以将其传递给View.

将ViewModel类放在自己的文件中,在自己的目录中是有意义的.在我的项目中,我有一个名为ViewModels的Models文件夹的子文件夹.这就是我的ViewModels(例如ProductViewModel.cs)所在的地方.



5> zihotki..:

没有好的地方可以保留你的模型.如果项目很大并且有很多ViewModel(数据传输对象),你可以将它们保存在单独的程序集中.您也可以将它们保存在站点项目的单独文件夹中.例如,在Oxite中,它们被放置在Oxite项目中,该项目也包含许多不同的类.Oxite中的控制器被移动到单独的项目中,视图也在单独的项目中.
在CodeCampServer中, ViewModel被命名为*Form,它们被放置在Models文件夹中的UI项目中.
在MvcPress项目中,它们被放置在Data项目中,该项目还包含与数据库一起使用的所有代码以及更多(但我不推荐这种方法,它仅用于示例)
所以你可以看到有很多观点.我通常将我的ViewModel(DTO对象)保存在站点项目中.但是,当我有超过10个模型时,我更愿意将它们移动到单独的组件中.通常在这种情况下我也将控制器移动到单独的组件中.
另一个问题是如何轻松地将模型中的所有数据映射到ViewModel.我建议看一下AutoMapper库.我非常喜欢它,它为我做了所有肮脏的工作.
我还建议你看一下SharpArchitecture项目.它为项目提供了非常好的架构,它包含许多很酷的框架和指南以及很棒的社区.


ViewModels!= DTO

6> Omu..:

这是我最佳实践的代码片段:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder createBuilder;
        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");
        }
}



7> Beep beep..:

我们将所有ViewModel抛出到Models文件夹中(我们所有的业务逻辑都在一个单独的ServiceLayer项目中)

推荐阅读
低调pasta_730
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有