asp.net-mvc ViewModel 最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/664205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ViewModel Best Practices
提问by jerhinesmith
From this question, it looks like it makes sense to have a controller create a ViewModelthat more accurately reflects the model that the view is trying to display, but I'm curious about some of the conventions (I'm new to the MVC pattern, if it wasn't already obvious).
从这个问题来看,让控制器创建一个更准确地反映视图试图显示的模型的ViewModel似乎是有意义的,但我对一些约定很好奇(我是 MVC 模式的新手,如果这还不是很明显)。
Basically, I had the following questions:
基本上,我有以下问题:
- I normally like to have one class/file. Does this make sense with a ViewModelif it is only being created to hand off data from a controller to a view?
- If a ViewModeldoes belong in its own file, and you're using a directory/project structure to keep things separate, where does the ViewModelfile belong? In the Controllersdirectory?
- 我通常喜欢有一个类/文件。如果创建ViewModel只是为了将数据从控制器传递到视图,这对ViewModel有意义吗?
- 如果ViewModel确实属于它自己的文件,并且您使用目录/项目结构来保持独立,那么ViewModel文件属于哪里?在控制器目录中?
That's basically it for now. I might have a few more questions coming up, but this has been bothering me for the last hour or so, and I can seem to find consistent guidance elsewhere.
目前基本上就是这样。我可能还有几个问题要问,但这在过去一个小时左右一直困扰着我,我似乎可以在其他地方找到一致的指导。
EDIT:Looking at the sample NerdDinner appon CodePlex, it looks like the ViewModels are part of the Controllers, but it still makes me uncomfortable that they aren't in their own files.
编辑:查看CodePlex上的示例NerdDinner 应用程序,看起来 ViewModels 是Controllers 的一部分,但它们不在自己的文件中仍然让我感到不舒服。
采纳答案by Ryan Montgomery
I create what I call a "ViewModel" for each view. I put them in a folder called ViewModels in my MVC Web project. I name them after the controller and action (or view) they represent. So if I need to pass data to the SignUp view on the Membership controller I create a MembershipSignUpViewModel.cs class and put it in the ViewModels folder.
我为每个视图创建了我所谓的“ViewModel”。我将它们放在我的 MVC Web 项目中名为 ViewModels 的文件夹中。我以它们代表的控制器和动作(或视图)命名它们。因此,如果我需要将数据传递到 Membership 控制器上的 SignUp 视图,我会创建一个 MembershipSignUpViewModel.cs 类并将其放在 ViewModels 文件夹中。
Then I add the necessary properties and methods to facilitate the transfer of data from the controller to the view. I use the Automapper to get from my ViewModel to the Domain Model and back again if necessary.
然后我添加必要的属性和方法来促进数据从控制器到视图的传输。我使用 Automapper 从我的 ViewModel 获取到域模型,并在必要时返回。
This also works well for composite ViewModels that contain properties that are of the type of other ViewModels. For instance if you have 5 widgets on the index page in the membership controller, and you created a ViewModel for each partial view - how do you pass the data from the Index action to the partials? You add a property to the MembershipIndexViewModel of type MyPartialViewModel and when rendering the partial you would pass in Model.MyPartialViewModel.
这也适用于包含属于其他 ViewModel 类型的属性的复合 ViewModel。例如,如果您在成员资格控制器的索引页面上有 5 个小部件,并且您为每个局部视图创建了一个 ViewModel - 您如何将数据从索引操作传递给局部视图?您向 MyPartialViewModel 类型的 MembershipIndexViewModel 添加一个属性,并且在呈现部分时您将传入 Model.MyPartialViewModel。
Doing it this way allows you to adjust the partial ViewModel properties without having to change the Index view at all. It still just passes in Model.MyPartialViewModel so there is less of a chance that you will have to go through the whole chain of partials to fix something when all you're doing is adding a property to the partial ViewModel.
这样做可以让您调整部分 ViewModel 属性,而无需更改 Index 视图。它仍然只是传入 Model.MyPartialViewModel,因此当您所做的只是向部分 ViewModel 添加属性时,您必须遍历整个部分链来修复某些内容的可能性较小。
I will also add the namespace "MyProject.Web.ViewModels" to the web.config so as to allow me to reference them in any view without ever adding an explicit import statement on each view. Just makes it a little cleaner.
我还将命名空间“MyProject.Web.ViewModels”添加到 web.config,以便我可以在任何视图中引用它们,而无需在每个视图上添加显式导入语句。只是让它更干净一点。
回答by Max Toro
Separating classes by category (Controllers, ViewModels, Filters etc.) is nonsense.
按类别(控制器、视图模型、过滤器等)分隔类是无稽之谈。
If you want to write code for the Home section of your website (/) then create a folder named Home, and put there the HomeController, IndexViewModel, AboutViewModel, etc. and all related classes used by Home actions.
如果你想为你网站的 Home 部分(/)编写代码,那么创建一个名为 Home 的文件夹,并将 HomeController、IndexViewModel、AboutViewModel 等以及 Home 操作使用的所有相关类放在那里。
If you have shared classes, like an ApplicationController, you can put it at the root of your project.
如果你有共享类,比如 ApplicationController,你可以把它放在你的项目的根目录下。
Why separate things that are related (HomeController, IndexViewModel) and keep things together that have no relation at all (HomeController, AccountController) ?
为什么要将相关的事物(HomeController、IndexViewModel)分开,而将根本没有关系的事物(HomeController、AccountController)放在一起?
I wrote a blog postabout this topic.
回答by Mark
I keep my application classes in a sub folder called "Core" (or a seperate class library) and use the same methods as the KIGGsample application but with some slight changes to make my applications more DRY.
我将我的应用程序类保存在一个名为“Core”(或一个单独的类库)的子文件夹中,并使用与KIGG示例应用程序相同的方法,但做了一些细微的更改以使我的应用程序更加 DRY。
I create a BaseViewData class in /Core/ViewData/ where I store common site wide properties.
我在 /Core/ViewData/ 中创建了一个 BaseViewData 类,我在其中存储了常见的站点范围的属性。
After this I also create all of my view ViewData classes in the same folder which then derive from BaseViewData and have view specific properties.
在此之后,我还在同一个文件夹中创建了我的所有视图 ViewData 类,然后这些类从 BaseViewData 派生并具有视图特定的属性。
Then I create an ApplicationController that all of my controllers derive from. The ApplicationController has a generic GetViewData Method as follows:
然后我创建了一个 ApplicationController,我的所有控制器都从它派生而来。ApplicationController 有一个通用的 GetViewData 方法,如下所示:
protected T GetViewData<T>() where T : BaseViewData, new()
{
var viewData = new T
{
Property1 = "value1",
Property2 = this.Method() // in the ApplicationController
};
return viewData;
}
Finally, in my Controller action i do the following to build my ViewData Model
最后,在我的控制器操作中,我执行以下操作来构建我的 ViewData 模型
public ActionResult Index(int? id)
{
var viewData = this.GetViewData<PageViewData>();
viewData.Page = this.DataContext.getPage(id); // ApplicationController
ViewData.Model = viewData;
return View();
}
I think this works really well and it keeps your views tidy and your controllers skinny.
我认为这非常有效,它可以使您的视图保持整洁,并使您的控制器保持精简。
回答by JMS
A ViewModel class is there to encapsulate multiple pieces of data represented by instances of classes into one easy to manage object that you can pass to your View.
ViewModel 类用于将类实例表示的多条数据封装到一个易于管理的对象中,您可以将其传递给您的视图。
It would make sense to have your ViewModel classes in their own files, in the own directory. In my projects I have a sub-folder of the Models folder called ViewModels. That's where my ViewModels (e.g. ProductViewModel.cs) live.
将您的 ViewModel 类放在自己的文件中,在自己的目录中是有意义的。在我的项目中,我有一个名为 ViewModels 的 Models 文件夹的子文件夹。那就是我的 ViewModels(例如ProductViewModel.cs)所在的地方。
回答by zihotki
There are no good place to keep your models in. You can keep them in separate assembly if the project is big and there are a lot of ViewModels (Data Transfer Objects). Also you can keep them in separate folder of the site project. For example, in Oxitethey are placed in Oxite project which contains a lot of various classes too. Controllers in Oxite are moved to separate project and views are in separate project too.
In CodeCampServerViewModels are named *Form and they are placed in UI project in Models folder.
In MvcPressproject they are placed in Data project, which also contains all code to work with database and a bit more (but I didn't recommend this approach, it's just for a sample)
So you can see there are many point of view. I usually keep my ViewModels (DTO objects) in the site project. But when I have more than 10 models I prefer to move them to separate assembly. Usually in this case I'm moving controllers to separate assembly too.
Another question is how to easily map all data from model to your ViewModel. I suggest to have a look at AutoMapperlibrary. I like it very much, it does all dirty work for me.
And I also I suggest to look at SharpArchitectureproject. It provides very good architecture for projects and it contains a lot of cool frameworks and guidances and great community.
没有什么地方可以保存您的模型。如果项目很大并且有很多 ViewModel(数据传输对象),您可以将它们保存在单独的程序集中。您也可以将它们保存在站点项目的单独文件夹中。例如,在Oxite 中,它们被放置在 Oxite 项目中,该项目也包含许多不同的类。Oxite 中的控制器移动到单独的项目中,视图也位于单独的项目中。
在CodeCampServer 中,ViewModel 被命名为 *Form,它们被放置在 Models 文件夹中的 UI 项目中。
在MvcPress项目中,它们被放置在 Data 项目中,该项目还包含所有用于数据库的代码以及更多(但我不推荐这种方法,它只是一个示例)
所以你可以看到有很多观点。我通常将我的 ViewModel(DTO 对象)保存在站点项目中。但是当我有超过 10 个模型时,我更喜欢将它们移动到单独的组件中。通常在这种情况下,我也会将控制器移动到单独的组件中。
另一个问题是如何轻松地将模型中的所有数据映射到您的 ViewModel。我建议看看AutoMapper库。我非常喜欢它,它为我做了所有肮脏的工作。
我还建议查看SharpArchitecture项目。它为项目提供了非常好的架构,并且包含了很多很酷的框架和指南以及很棒的社区。
回答by Omu
here's a code snippet from my best practices:
这是我的最佳实践中的代码片段:
public class UserController : Controller
{
private readonly IUserService userService;
private readonly IBuilder<User, UserCreateInput> createBuilder;
private readonly IBuilder<User, UserEditInput> editBuilder;
public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> 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");
}
}
回答by Beep beep
We throw all of our ViewModels in the Models folder (all of our business logic is in a separate ServiceLayer project)
我们将所有的 ViewModels 扔到 Models 文件夹中(我们所有的业务逻辑都在一个单独的 ServiceLayer 项目中)
回答by Preet Sangha
Personally I'd suggest if the ViewModel is anything but trivial then use a separate class.
我个人建议如果 ViewModel 不是微不足道的,那么使用单独的类。
If you have more than one view model then I suggest it make sense to partition it in at least a directory. if the view model is later shared then the name space implied in the directory makes it easier to move to a new assembly.
如果您有多个视图模型,那么我建议至少将其分区在一个目录中。如果稍后共享视图模型,则目录中隐含的名称空间可以更轻松地移动到新程序集。
回答by Marc Climent
In our case we have the Models along with the Controllers in a project separate from the Views.
在我们的例子中,我们在与视图分开的项目中拥有模型和控制器。
As a rule of thumb, we've tried to move and avoid most of the ViewData["..."] stuff to the ViewModel thus we avoid castings and magic strings, which is a good thing.
根据经验,我们已经尝试将大部分 ViewData["..."] 内容移动并避免到 ViewModel 中,因此我们避免了强制转换和魔术字符串,这是一件好事。
The ViewModel as well holds some common properties like pagination information for lists or header information of the page to draw breadcrumbs and titles. At this moment the base class holds too much information in my opinion and we may divide it in three pieces, the most basic and necessary information for 99% of the pages on a base view model, and then a model for the lists and a model for the forms that hold specific data for that scenarios and inherit from the base one.
ViewModel 还拥有一些常见的属性,例如列表的分页信息或页面的标题信息以绘制面包屑和标题。目前我认为基类包含的信息太多,我们可以将其分为三部分,基本视图模型上 99% 的页面的最基本和必要的信息,然后是列表的模型和模型用于保存该场景的特定数据并从基础数据继承的表单。
Finally, we implement a view model for each entity to deal with the specific information.
最后,我们为每个实体实现一个视图模型来处理特定的信息。
回答by Ajay Kelkar
Create a view model base class which has commonly required properties like result of the operation and contextual data ,you can also put current user data and roles
创建一个视图模型基类,它具有操作结果和上下文数据等常用属性,您还可以放置当前用户数据和角色
class ViewModelBase
{
public bool HasError {get;set;}
public string ErrorMessage {get;set;}
public List<string> UserRoles{get;set;}
}
In base controller class have a method like PopulateViewModelBase() this method will fill up the contextual data and user roles. The HasError and ErrorMessage , set these properties if there is exception while pulling data from service/db. Bind these properties on view to show error. User roles can be used to show hide section on view based on roles.
在基本控制器类中有一个像 PopulateViewModelBase() 这样的方法,这个方法将填充上下文数据和用户角色。HasError 和 ErrorMessage ,如果在从 service/db 中提取数据时出现异常,请设置这些属性。在视图中绑定这些属性以显示错误。用户角色可用于根据角色在视图上显示隐藏部分。
To populate view models in different get actions , it can be made consistent by having base controller with abstract method FillModel
要在不同的 get 操作中填充视图模型,可以通过使用具有抽象方法 FillModel 的基本控制器来使其保持一致。
class BaseController :BaseController
{
public PopulateViewModelBase(ViewModelBase model)
{
//fill up common data.
}
abstract ViewModelBase FillModel();
}
In controllers
在控制器中
class MyController :Controller
{
public ActionResult Index()
{
return View(FillModel());
}
ViewModelBase FillModel()
{
ViewModelBase model=;
string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString();
try
{
switch(currentAction)
{
case "Index":
model= GetCustomerData();
break;
// fill model logic for other actions
}
}
catch(Exception ex)
{
model.HasError=true;
model.ErrorMessage=ex.Message;
}
//fill common properties
base.PopulateViewModelBase(model);
return model;
}
}

