MVC ViewTemplate和ModelMetadata详解-MVC原理系列10

本文探讨了ASP.NET MVC中的TemplatedViewHelpers及ModelMetadata的使用,介绍了如何通过这些工具实现HTML元素的智能化生成及模型元数据的管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上节:ASP.NET MVC Controller异步机制-MVC原理系列9,讲述了ASP.NET MVC Controller 异步机制的相关知识。

本节你讲解:ViewTemplate和ModelMetadata这两方面的内容,让你更深入的理解ASP.NET MVC的原理,从而更有助于自已实现MVC扩展,创造出属于自己的模块。

今天的内容主要为:

  • Templated view helpers:根据Model生成Html控件元素
  • Model Binding:自动映射和解析用户提交的数据
  • Integrating validation:集成客户端认证

ASP.NET web应用程序的数据交互其实就是客户端表单数据和.NET对象(Model)之间的转化。下图说明了这个问题:

clip_image002

在MVC中,众多HTML Helper负责将Model转化成Html标记,Model binding将用户提交的数据转化成Model。

 

Templated View Helpers

MVC2新增的Templated View Helpers指的是类似Html.TextBoxFor()之类的扩展方法。用这样的方法来构造链接表单之类的的Html元素的话,是十分方便和智能的。这些方法会根据Model或Model属性的类型自动决定转换成什么样的Html元素,并自动使得Model Binding得以支持。

比如如果你有个属性叫Approved,是个bool类型,那么Html.EditorFor(x => x.Approved)将会转化成一个check box。比如当调用Html.EditorFor()时,MVC需要选择一个合适的模板呈现,因此模板可以理解成对某种数据结构的预定义的Html的呈现方式。先来看看MVC内建有哪些模板,下面的代码是从TemplateHelpers中摘录的:

static readonly Dictionary<string, Func<HtmlHelper, string>> defaultDisplayActions =            new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {                { "EmailAddress",       DefaultDisplayTemplates.EmailAddressTemplate },                { "HiddenInput",        DefaultDisplayTemplates.HiddenInputTemplate },                { "Html",               DefaultDisplayTemplates.HtmlTemplate },                { "Text",               DefaultDisplayTemplates.StringTemplate },                { "Url",                DefaultDisplayTemplates.UrlTemplate },                { "Collection",         DefaultDisplayTemplates.CollectionTemplate },                { typeof(bool).Name,    DefaultDisplayTemplates.BooleanTemplate },                { typeof(decimal).Name, DefaultDisplayTemplates.DecimalTemplate },                { typeof(string).Name,  DefaultDisplayTemplates.StringTemplate },                { typeof(object).Name,  DefaultDisplayTemplates.ObjectTemplate },            };        static readonly Dictionary<string, Func<HtmlHelper, string>> defaultEditorActions =            new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {                { "HiddenInput",        DefaultEditorTemplates.HiddenInputTemplate },                { "MultilineText",      DefaultEditorTemplates.MultilineTextTemplate },                { "Password",           DefaultEditorTemplates.PasswordTemplate },                { "Text",               DefaultEditorTemplates.StringTemplate },                { "Collection",         DefaultEditorTemplates.CollectionTemplate },                { typeof(bool).Name,    DefaultEditorTemplates.BooleanTemplate },                { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },                { typeof(string).Name,  DefaultEditorTemplates.StringTemplate },                { typeof(object).Name,  DefaultEditorTemplates.ObjectTemplate },            };

从中可以看到内建的模板有哪些。总的来说,Html元素可以分为两类,显示类和编辑类。分别地,主要有两大类的Helper,DisplayXXX、LabelXXX和EditorXXX。MVC框架包含有DefaultDisplayTemplates和DefaultEditorTemplates分别负责完成render的工作。而TemplateHelpers类负责调配选择使用那种模板。MVC内建的模板主要是简单类型,对于复杂类型MVC支持用户自己定义模板,自定义一个模板实际上就是创建ascx,然后放在合适的位置,由View引擎自行查找。框架会审查/Views/Shared/DisplayTemplates//Views/Shared/EditorTemplates/目录下的ascx文件,并将其视为自定义模板加载,所以我们可以设计复杂的ascx模板,将他视为用户控件,然后简单的使用TemplateHelper的各种方法来加载,这也不失为一种代替PartialView或ChildAction的方式。这里的ascx文件名要尽量对应类型名,因此,我们可以创建一个DateTime.ascx,并像下面这样编辑代码来“重载”MVC内建对DateTime数据类型的模板。

<%@ Control Language="C#" Inherits="ViewUserControl<DateTime?>" %> <%: Html.TextBox("",                                        /* Name suffix     */                  ViewData.TemplateInfo.FormattedModelValue, /* Initial value   */                  new { @class = "date-picker" }             /* HTML attributes */     ) %>

注意到,这里用到ViewData.TemplateInfo.FormattedModelValue,而不是Model.ToString(),这样可以充分利用ModelMetadata的特性,关于ModelMetaData将在以后更多的涉及。这里也可以这样写:

<%@ Control Language="C#" Inherits="ViewTemplateUserControl<DateTime?>" %> <%: Html.TextBox("",                             /* Name suffix     */                  FormattedModelValue,            /* Initial value   */                  new { @class = "date-picker" }  /* HTML attributes */ ) %>

注意到这里继承的是ViewTemplateUserControl而不是上面的ViewUserControl。

  1. 既然MVC以模板的方式呈现UI,那么是什么影响MVC的选择呢?以下因素按优先级影响到MVC框架对模板的选择:
  2. 在EditorFor方法中显示指定的模板名称,Html.EditorFor(x => x.SomeProperty , “My Template”)。
  3. 对应Model的元数据描述,比如在属性上添加特性[UIHint(“My Template”)]
  4. Model的元数据描述的数据类型,比如[DataType(DataType.EmailAddress)]
  5. 对应属性的真实.NET 类型
  6. 对于可以被转化成string的简单类型,使用String模板
  7. Model的父类属性也会被转化
  8. 如果属性实现了IEnumable,将选择Collection模板
  9. 最后使用Object模板

 

ModelMetadata

TemplateHelpers的确是完成render工作的最主要类,但事实上TemplateHelpers也仅仅负责render,它需要一个叫ModelMetadata的东西来为它提供数据,而ModelMetadata本身就像它的类名,意思是“模型元数据”,相当于一个描述数据的对象,这个对象需要ModelMetadataProvider来提供真正提供数据,下图可以帮助理解:

clip_image002[5]

可以看到MVC内建了DataAnnotationModelMetadataProvider来充当ModelMetadataProvider,它内建支持.NET中的Data Annotation特性,比如DisplayColumDisplayFormatRequired等,不仅如此DataAnnotationModelMetadataProvider还支持MVC特有的特性描述如:UIHint等。

可以像下面这样指定一个ModelMetadataProvider:ConventionsMetadataProvider。

protected void Application_Start() {     AreaRegistration.RegisterAllAreas();     RegisterRoutes(RouteTable.Routes);     ModelMetadataProviders.Current = new ConventionsMetadataProvider(); }

ModelMetadataProvider本身是个抽象类,可以从下面任意的类中继承,推荐从DataAnnotationModelMetadataProvider继承,这样我们自定义的ModelMetadataProvider就能保留原有的支持了。

image

再来大概看看MVC中ModelMetadata有哪些属性和方法,它们中的部分将被TemplateHelpers考察并影响到Html元素的呈现。其中的FromLambdaExpression()方法用于从Lambda表达式中得到ModelMetadata,这也是内建的TemplateHelpers要调用的方法。

image

 

当我们需要用Attribute描述Model类的时候,也许会碰到这样的情况,Model本身是诸如ORM等工具生成的,不能直接修改这样的类。于是MVC提供了[MetadataType]属性来解决这种情形。通常自动生成的Model类是部分类:

public partial class Person {     public int PersonId { get; set; }     public string FirstName { get; set; }     public string LastName { get; set; }     public DateTime BirthDate { get; set; }     public Address HomeAddress { get; set; }     public bool IsApproved { get; set; } }

定义另一个对应的类,并用MetadataType特性标识,在其中定义一个内部类,标注上需要的特性描述即可。

[MetadataType(typeof(PersonMetadata))] public partial class Person {     // This class is only used as a source of metadata     private class PersonMetadata     {         [HiddenInput(DisplayValue = false)] public int PersonId { get; set; }         [DisplayName("First name")] public string FirstName { get; set; }         [DisplayName("Last name")] public string LastName { get; set; }          // Also add any other properties for which you want to supply metadata     } }


http://www.th7.cn/Program/net/201307/143648.shtml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值