使用ViewModels在ASP.NET MVC应用程序中管理数据和组织代码
ViewModel的概念不仅适用于ASP.NET MVC,因为您将在整篇网页中看到有关MVC,MVP和MVVM模式的文章和博客文章中对ViewModels的引用。这些帖子和文章可以围绕任何数量的技术,如ASP.NET,Silverlight,WPF或MVC ...本文将调查ViewModels适用于ASP.NET MVC的世界。
什么是ASP.NET MVC ViewModel?
在ASP.NET MVC中,ViewModels允许您将来自一个或多个数据模型或源的多个实体变形为单个对象,并针对视图的消耗和渲染进行优化。下图展示了ViewModel的概念:
ViewModel的目的是让视图具有单个对象进行渲染,从而减少视图中对UI逻辑代码的需求,否则这些代码是必需的。这意味着该视图的唯一责任或关注点是呈现该单个ViewModel对象,帮助更清晰地分离关注点(SoC)。关注点是具有特定目的(即关注点)的应用程序的不同方面,并且保持这些方面不同意味着您的应用程序更加有组织,代码更专注。将数据操作代码放在远离视图和控制器的位置,强制执行SoC。
在MVC中使用ViewModels可实现更精细的粒度和更好的SoC,从而提供更易于维护和可测试的代码。请记住,单元测试是关于测试小单元的。
随着更好的编码实践,有很多商业原因说明为什么你可能会考虑使用ViewModels:
- 将查询数据的下拉列表合并到相关实体中
- 主细节记录视图
- 分页:结合实际数据和分页信息
- 像购物车或用户配置文件小部件这样的组件
- 仪表板,具有多个不同数据源
- 报告,通常包含汇总数据
上述场景对于各种应用程序都很常见,并且处理比基本的CRUD格式的数据页面更复杂的数据(例如,简单的1:1映射到数据库表)。例如,提供状态列表,并确保与当前客户状态相匹配的状态,意味着您需要提供两组数据或一组客户/状态数据,如下图所示。
某些场景(如代表美国状态的查找表)可以轻松地与ViewModels或ViewBag / ViewData对象一起使用,因此有时可能会有一些重叠。由应用程序架构师和开发人员决定什么对他们的确切用例最有效。
创建一个ViewModel
尽管一个ViewModel由多个实体组成,但ViewModel的核心仍然只是一个类 - 并且它甚至不会像许多MVC类那样继承任何特殊的东西。
从物理上看,ViewModels可以存在于不同的位置,如下所示:
- 在名为ViewModels的文件夹中,该文件夹驻留在项目的根目录中。(小应用程序)
- 作为从MVC项目引用的.dll(任何大小的应用程序)
- 在作为服务层的单独项目中,用于生成视图/内容特定数据的大型应用程序。(企业应用程序)
由于ViewModel只是一个类,因此开始使用ViewModel的最简单方法是创建一个名为ViewModels的新文件夹并为其添加一个新的代码文件。
要创建CustomerViewModel ViewModel,请将Customer和StatesDictionary类型添加为属性以形成一个CustomerViewModel类。在下面的示例中,CustomerViewModel类包含新定义的属性。
公共 类 CustomerViewModel
{
公共客户{get; 组; }
public StatesDictionary States {get; 组; }
公共 CustomerViewModel(客户客户)
{
客户=客户;
States = new StatesDictionary();
}
}
通常,ViewModels包含单词“ViewModel”作为其名称的一部分; 然而,在这里工作的约定是为了保持代码可读性的一致性,因为MVC中的其他类也在其名称中声明了它们的意图(例如,控制器的名称,操作方法等...在名称中使用约定)。
StatesDictionary类是一个简单的Dictionary对象,它包含两个字符串类型的参数。该类还包含词典中所有成员的定义(即状态数据)。StatesDictionary类中唯一的属性是StateSelectList,它是Html Helpers用来呈现显示状态列表的HTML <select>元素的对象。StateSelectList属性中的类型Dictionary <string,string>分别映射到状态缩写和状态名称。
公共 类 StatesDictionary
{
公共 静态 SelectList StateSelectList
{
get { return new SelectList(StateDictionary,“Value”,“Key”); }
}
public static readonly IDictionary < string,string >
StateDictionary = new Dictionary < string,string > {
{ “选择...”,“” }
,{ “Alabama”,“AL” }
,{ “阿拉斯加”,“AK” }
,{ “Arizona”,“AZ” }
,{ “阿肯色州”,“AR” }
,{ “California”,“CA” }
//代码继续添加状态...
};
}
存在于所有类型的应用程序中的数据位于小列表中,并且不常发生变化,如StatesDictionary类。在现实世界的应用程序中,您会发现处理查找数据的各种方法,例如状态列表(通常是XML文件和SQL表)。您可以将StateDictionary方法中的代码替换为使用Entity Framework中的实体,从文件中读取数据或您需要的任何数据访问代码。
创建ViewModel后,接下来的步骤是在控制器中实例化它并将其返回到视图。
获取ViewModel的视图
从控制器开始...
将ViewModel发送到视图进行渲染将与处理模型时的工作方式相同。由于它只是一个类,视图不知道模型或ViewModel来自哪里,并且不关心。您可以在控制器中创建ViewModel类的实例,或者在使用IoC容器时解析它。请记住,就像您使用视图一样,您应该让控制器清理不必要的代码,这意味着只有取得模型或ViewModel的代码才属于此处,并且只有更多。
public ActionResult Edit(int id)
{
Customer customer = context.Customers.Single(x => x.Id == id);
var customerViewModel = new CustomerViewModel(customer);
返回 View(customerViewModel);
}
然后该视图呈现ViewModel ...
为了使视图知道要使用哪个对象,请将@model关键字设置为指向ViewModel,就像您对常规模型已经使用的那样。
@model FourthCoffee.Web.ViewModels.CustomerViewModel
由于Customer对象是ViewModel的属性,因此您将看到model.Class.Property语法来访问ViewModel数据,与以下代码行类似。
< div class =“editor-label” >
@ Html.LabelFor(model = > model.Customer.FirstName)
</ div >
< div class =“editor-field” >
@ Html.EditorFor(model = > model.Customer.FirstName)
@ Html.ValidationMessageFor(model = > model.Customer.FirstName)
</ div >
@ * ...查看代码继续渲染属性... * @
此外,您可以编辑编辑/创建视图,以便包含状态列表的DropDownList将显示,并显示与客户相匹配的正确状态。
< div class =“editor-field” >
@ Html.DropDownList(“状态”,新的SelectList(StatesDictionary.StateSelectList,
“Value”,“Text”,Model.Customer == null?“”:Model.Customer.State))
@ Html.ValidationMessageFor(model = > model.Customer.State)
</ div >
检查结果
在用户导航到浏览器中的/ Customers / Edit / 1 URL后,Razor视图引擎将呈现CustomerViewModel,类似于以下屏幕快照。
按照预期,状态DropDownList显示该客户的状态和当前状态。
深入研究ViewModels
由于ViewModel渲染的模型类和数据库表之间不再具有这些1:1映射的预处理数据,因此您需要自己创建映射。您可以手动映射小型ViewModels,但在映射较大的类时,特别是在处理父子孙,多级或复杂数据时,这会很快变得繁重。这就是AutoMapper等工具发挥作用的地方。通过AutoMapper,您可以更轻松地手动设置ViewModel和模型之间的映射,或者编写您自己的映射器。
以下是使用ViewModels的一些技巧:
- 仅放入将在ViewModel中呈现的数据。
- 该视图应该指导ViewModel的属性,这样它就更适合渲染和维护。
- 当ViewModel变得复杂时使用映射器。
一些可以帮助您为模型和ViewModel生成POCO(Plain Old CLR对象)的工具是:
除了这些工具,您还可以使用MvcScaffolding根据ViewModel创建操作和视图。MvcScaffolding,ASP.NET团队成员Steve Sanderson的发明,可以让您更轻松快捷地创建CRUD,存储库,单元测试和其他模板。点击此处查看Steve的MvcScaffolding多部分系列。MvcScaffolding与ViewModels以及模型一起使用。
您应该始终更喜欢使用ViewModel,而不是实例化多个模型并将该操纵代码放入控制器中。
概要
当您需要处理比其他对象允许的更复杂的数据时,ViewModels可帮助您组织和管理MVC应用程序中的数据。使用ViewModels可以让您灵活地使用您认为合适的数据。ViewModels区域通常比模型+ ViewBag / ViewData对象更灵活地访问多个数据源。