ASP.NET MVC的Model元数据与Model模板:模板的获取与执行策略
当我们调用HtmlHelper或者HtmlHelper TModel 的模板方法对整个Model或者Model的某个数据成员以某种模式(显示模式或者编辑模式)进行呈现的时候,通过预先创建的代表Model元数据的ModelMetadata对象都可以找到相应的模板。如果模板对应着某个自定义的分部View,那么只需要执行该View即可;对于默认模板,则直接可以得到相应的HTML。本篇文章着重讨论模板的获取和执行机制,不过在这之前,顺便来讨论一下DataTypeAttribute和模板的关系。[本文已经同步到《How ASP.NET MVC Works?》中]
一、 DataTypeAttribute和模板有何关系?通过《初识Model元数据》针对Model元数据定义的介绍,我们知道通过DataTypeAttribute特性对目标元素设置的数据类型最终会反映在表示Model元数据的ModelMetadata对象的DataTypeName属性上。此外,对于某些设置的数据类型,比如Date、Time、Duration和Currency等,还会随之创建一个DisplayFormatAttribute应用到ModelMetadata上。那么ModelMetadata的DataTypeName属性对目标元素的最终呈现具有怎样的影响呢?
实际上在模板匹配的过程中会将ModelMetadata的DataTypeName属性当作模板名称来看待,所以下面两种形式的Model类型定义可以看成是等效的。通过UIHintAttribute特性设置的模板名称和通过DataTypeAttribute特性设置的数据类型的唯一不同之处在于前者具有更高的优先级。换句话说,如果将UIHintAttribute和DataTypeAttribute同时应用到同一个数据成员分别将模板名称和数据类型设置为ABC和123,自定义模板123只有在模板ABC不存在的情况下才会被使用。
实例演示:证明DataTypeName与模板名称的等效性
为了证明通过DataTypeAttribute特性设置数据类型在针对目标元素进行可视化呈现过程中被视为模板名称,我们来做一个简单的实例演示。在这个实例中我们定义了如下一个表示三角形的数据类型Triangle,其属性A、B和C是一个Point对象,表示三个角所在的坐标。
33: if (!double.TryParse(split[0], out x) ||!double.TryParse(split[1], out y))
34: {
35: throw new FormatException("Invalid point expression.");
36: }
37: return new Point(x, y);
38: }
39: }
40:
41: public class PointTypeConverter : TypeConverter
42: {
43: public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType)
44: {
45: return sourceType == typeof(string);
46: }
47:
48: public override object ConvertFrom(ITypeDescriptorContext context,CultureInfo culture, object value)
49: {
50: if (value is string)
51: {
52: return Point.Parse(value as string);
53: }
54: return base.ConvertFrom(context, culture, value);
55: }
56: }
对于类型Triangle和Point的定义,有两点值得注意:其一,Triangle的三个A、B和C属性上应用了DataTypeAttribute特性并将自定义数据类型设置为PointInfo(不是Point);其二,Point类型上应用了TypeConverterAttribute特性并将TypeConverter类型设置为PointTypeConverter,后者支持源自字符串的类型转换。通过前面对复杂类型(Complex Type)的介绍,这样会将Triangle的三个属性从复杂类型成员转换成简单类型成员。根据前提介绍的关于Object模板对数据成员的便利规则,Triangle的这三个属性才能被最终呈现出来。
现在我们创建一个Model类型为Point的强类型分部View作为模板,并将其命名为PointInfo(和前面通过DataTypeAttribute特性指定的自定义数据类型一致)。我们只为Point定义关于显示模式的模板,所以我们将该分部View文件放在Views\Shared\DisplayTemplates中。如下面的代码片断所示,我们将一个Point对象显示为(X,Y)的形式。
现在我们创建一个默认的HomeCtroller。如下面的代码片断所示,在默认的Index操作方法中我们创建了一个Triangle对象将其呈现在默认的View中。
下面是对应的View的定义,这是一个Model类型为Triangle的强类型View,我们仅仅调用了HtmlHelper TModel 的DisplayModel方法将作为Model的Triangle对象以显示模式呈现出来。
运行该Web应用会在浏览器中得到如下图所示的呈现效果,我们可以看到作为我们创建的Triangle对象的A、B和C属性表示的三个角的坐标是完全按照我们定义的PointInfo模板的方式进行呈现的。
二、模板的获取与执行当我们调用HtmlHelper或者HtmlHelper TModel 的模板方法对整个Model或者Model的某个数据成员以某种模式(显示模式或者编辑模式)进行呈现的时候,通过预先创建的代表Model元数据的ModelMetadata对象都可以找到相应的模板。如果模板对应着某个自定义的分部View,那么只需要执行该View即可;对于默认模板,则直接可以得到相应的HTML。
根据Model元数据对目标模板的解析是整个模板方法执行流程中最核心的部分,也是本篇讨论的重点。我们以针对HtmlHelper TModel 的扩展方法DisplayFor为例,看看针对通过表达式expression获取的Model对象是如何以显示模式呈现出来的。
3: public static MvcHtmlString DisplayFor TModel, TValue (this HtmlHelper TModel html, Expression Func TModel, TValue expression, string templateName);
4: }
在DisplayFor被调用的时候,如果通过参数expression表示的Model获取表达式是针对某个属性的,那么属性名会被获取出来。然后执行表达式得到一个作为Model的对象,该对象连同属性名(如果有)一起被用于表示Model元数据的Metadatadata对象。接下来会根据该Metadatadata对象得到一系列表示分部模板View名称的列表,这些View名称按照优先级排列如下:
作为参数templateName传入的模板名称(如果不为空)。 Metadatadata的TemplateHint属性值(如果不为空)。 Metadatadata的DataTypeName属性值(如果不为空)。 如果Model对象的真实类型为非空值类型,该类型名作为模板View名;否则底层(Underlying)类型名作为模板View名(比如说,对于int?类型则将Int32作为模板View名)。 如果Model对象的真实类型为非复杂类型,则使用String模板(由于非复杂类型能够实现与String类型之间的转换,所以可以转换成String进行呈现)。 在Model的声明类型为接口情况下,如果该接口继承自IEnuerable则采用Collection模板。 在Model的声明类型为接口情况下,使用Object模板。 如果Model声明类型不是接口类型,按照其类型继承关系向上追溯知道Object类型,逐个将类型名称作为模板View名称。如果声明类型实现了IEnuerable接口,则将最后的Object替换成Collection。对于得到的这个表示列表,会按照先后顺序便利所有的元素。针对具体的表示模板View名称的某个字符串,会根据呈现的模式在指定的路径(显示模式和编辑模式分别为“/DisplayTemplates/{TemplateName}”和“/EditorTemplates/{TemplateName}”)去寻找定义模板的View。如果这样的View存在,则直接执行该View并返回。如果不能找到自定义模板分部View,则根据该模板名称在默认的模板列表中查找,如果存在名称匹配的默认模板,则直接返回默认模板对应的HTML。如果默认的模板列表中的名称均与指定的名称不匹配,在进入下一次迭代。
ASP.NET MVC的Model元数据与Model模板:预定义模板
ASP.NET MVC的Model元数据与Model模板:模板的获取与执行策略
ASP.NET MVC的Model元数据与Model模板:将ListControl引入ASP.NET MVC
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 原文链接
基于Asp.Net Mvc开发的个人博客系统 一个基于Mvc 5构建的简单、代码层级分明的开源个人博客系统。前端美观大气、后台采用RightControl .NET通用角色权限系统,开发简单、效率高。网站配置采用XML配置,灵活可以根据自己是需求进行个性化配置。系统功能完备,完全可以满足需求,基本不用二次开发,非常使用程序员的个人博客。
相关文章
- 在Asp.Net Core中使用DI的方式使用Hangfire构建后台执行脚本
- ASP.NET开发,简化与封装
- 转载 Net多线程编程—System.Threading.Tasks.Parallel
- asp.net loading 动画
- ASP.NET Web API 应用教程(一) ——数据流使用
- ASP.NET项目开发详解
- ASP.NET 全局变量和页面间传值方法
- .NET 中 GC 的模式与风格
- (二)Asp.net web api中的坑-【http get请求中的参数】
- .NET(C#) .NET Framework中自带的泛型委托Func
- ASP.NET Core 2.1 HTTPClientFactory使用的3种方法
- ASP.NET Core中JsonResult和ObjectResult的用法
- ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存
- ASP.NET MVC的Model元数据与Model模板:预定义模板
- asp.net core 发布包含文件
- ASP.NET Core 连接 GitLab 与 MatterMost 打造 devops 工具
- 秒懂ASP.NET中的内置对象
- .Net快速获取网络文本文件最后一段文字-小应用
- .Net中队列类的操作与系统队列类queue的使用