MVC之Model元数据
Contronoller激活之后,ASP.NET MVC会根据当前请求上下文得到目标Action的名称,然后解析出对应的方法并执行之。
在整个Action方法的执行过程中,Model元数据的解析是一个非常重要的环节。ASP.NET MVC中的Model实际上View Model,表示最终绑定到View上的数据,而Model元数据描述了Model的数据结构,以及Model的每个数据成员的一些特性。
正是有了Model元数据的存在,才使模板化HTML的呈现机制成为可能。此外,Model元数据支撑了ASP.NET MVC的Model验证体系,因为针对Model的验证规则正是定义在Model元数据中。ASP.NET MVC的Model元数据通过类型ModelMetadata表示。
ModelMetadata通过一系列的属性描述了Model及其成员相关的元数据信息,在正式介绍这些元数据选项之前,我们很有必要先来了解一下Model元数据层次化结构。[《How ASP.NET MVC Works?》中]
目录
一、Model元数据层次化结构
二、基本Model元数据信息
三、Model元数据的定制
UIHintAttribute
HiddenInputAttribute与ScaffoldColumnAttribute
DataTypeAttribute与DisplayFormatAttribute
EditableAttribute与ReadOnlyAttribute
DisplayAttribute与DisplayNameAttribute
RequiredAttribute
四、IMetadataAware接口
AllowHtmlAttribute
实例演示:创建实现IMetadataAware接口的特性定制Model元数据
一、Model元数据层次化结构
作为Model的数据类型可以是一个简单的字符串或者是一个值类型的对象,也可能是一个复杂的数据类型。对于一个复杂的数据类型,基于类型本身和数据成员的元数据都通过一个ModelMetadata来表示,而某个数据成员又可能是一个复杂类型,所以通过ModelMetadata对象表示的Model元数据实际上具有一个树形层次化结构。
举个例子,我们具有一个具有如下定义的表示联系人的数据类型Contact。属性Name、PhoneNo、EmailAddress和Address分别代表姓名、电话号码、邮箱地址和联系地址。联系地址通过另一个数据类型Address表示,属性Province、City、District和Street分别表示所在省份、城市、城区和街道。
1: public class Contact
2: {
3: public string Name { get; set; }
4: public string PhoneNo { get; set; }
5: public string EmailAddress { get; set; }
6: public Address Address { get; set; }
7: }
8: public class Address
9: {
10: public string Province { get; set; }
11: public string City { get; set; }
12: public string District { get; set; }
13: public string Street { get; set; }
14: }
如果将Contact类型作为Model,作为其元数据的ModelMetadata不仅仅具有Contact类型本身和其属性成员的描述,由于其Address属性是一个复杂类型,元数据还需要描述定义在该类型中的4个属性成员。下图反映基于Contact类型的Model元数据的层次化结构。
表示Model元数据的ModelMetadata类型不仅用于描述某个作为Model的数据类型,还用于递归地描述其所有属性成员(不包含字段成员),所以ModelMetadata具有一个树型层次化结构,这也可以从ModelMetadata的定义可以看出来。
1: public class ModelMetadata
2: {
3: //其他成员
4: public virtual IEnumerable<ModelMetadata> Properties { get; }
5: }
如上面的代码片断所示,ModelMetadata具有一个类型为IEnumerable<ModelMetadata>的只读属性Properties,表示用于描述属性/字段成员的ModelMetadata集合。ModelMetadata的层次化结构可以通过如下图所示的UML来体现。由于基于类型的ModelMetadata和基于数据成员的ModelMetadata是一种包含关系,我们可以将前者称为后者的容器(Container)。
二、基本Model元数据信息
基于作为Model类型创建的元数据主要是为View实现模板化HTML呈现和数据验证服务的,我们可以通过在类型和数据成员上应用相应的特性控制Model在View中的呈现方式或者定义相应的验证规则。在介绍声明式Model元数据编程方式之前,我们先来介绍表示Model元数据的ModelMetadata类型中与UI呈现和数据验证无关的基本属性。
// Summary:
// No content here will be updated; please do not add material here.
public class ModelMetadata
{
// Summary:
// Initializes a new instance of the System.Web.Http.Metadata.ModelMetadata
// class.
//
// Parameters:
// provider:
// The provider.
//
// containerType:
// The type of the container.
//
// modelAccessor:
// The model accessor.
//
// modelType:
// The type of the model.
//
// propertyName:
// The name of the property.
public ModelMetadata(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName); // Summary:
// Gets a dictionary that contains additional metadata about the model.
//
// Returns:
// A dictionary that contains additional metadata about the model.
public virtual Dictionary<string, object> AdditionalValues { get; }
//
// Summary:
// Gets or sets the type of the container for the model.
//
// Returns:
// The type of the container for the model.
public Type ContainerType { get; }
//
// Summary:
// Gets or sets a value that indicates whether empty strings that are posted
// back in forms should be converted to null.
//
// Returns:
// true if empty strings that are posted back in forms should be converted to
// null; otherwise, false. The default value is true.
public virtual bool ConvertEmptyStringToNull { get; set; }
//
// Summary:
// Gets or sets the description of the model.
//
// Returns:
// The description of the model. The default value is null.
public virtual string Description { get; set; }
//
// Summary:
// Gets or sets a value that indicates whether the model is a complex type.
//
// Returns:
// A value that indicates whether the model is considered a complex.
public virtual bool IsComplexType { get; }
//
// Summary:
// Gets a value that indicates whether the type is nullable.
//
// Returns:
// true if the type is nullable; otherwise, false.
public bool IsNullableValueType { get; }
//
// Summary:
// Gets or sets a value that indicates whether the model is read-only.
//
// Returns:
// true if the model is read-only; otherwise, false.
public virtual bool IsReadOnly { get; set; }
//
// Summary:
// Gets the value of the model.
//
// Returns:
// The model value can be null.
public object Model { get; set; }
//
// Summary:
// Gets the type of the model.
//
// Returns:
// The type of the model.
public Type ModelType { get; }
//
// Summary:
// Gets a collection of model metadata objects that describe the properties
// of the model.
//
// Returns:
// A collection of model metadata objects that describe the properties of the
// model.
public virtual IEnumerable<ModelMetadata> Properties { get; }
//
// Summary:
// Gets the property name.
//
// Returns:
// The property name.
public string PropertyName { get; }
//
// Summary:
// Gets or sets the provider.
//
// Returns:
// The provider.
protected ModelMetadataProvider Provider { get; set; } // Summary:
// Gets the display name for the model.
//
// Returns:
// The display name for the model.
public string GetDisplayName();
//
// Summary:
// Gets a list of validators for the model.
//
// Parameters:
// validatorProviders:
// The validator providers for the model.
//
// Returns:
// A list of validators for the model.
public virtual IEnumerable<Web.Http.Validation.ModelValidator> GetValidators(IEnumerable<Web.Http.Validation.ModelValidatorProvider> validatorProviders);
ModelMetadata
1: public class ModelMetadata
2: {
3: //其他成员
4: public Type ModelType { get; }
5: public virtual bool IsComplexType { get; }
6: public bool IsNullableValueType { get; }
7: public Type ContainerType { get; }
8:
9: public object Model { get; set; }
10: public string PropertyName { get; }
11:
12: public virtual Dictionary<string, object> AdditionalValues { get; }
13: protected ModelMetadataProvider Provider { get; set; }
14: }
如上面的代码片断所示,ModelMetadata具有四个类型相关的只读属性。ModelType表示Model本身的类型,比如说针对上面定义的Contact类型的ModelMetadata对象,其ModelType属性值就是Contact类型;而针对其属性的ModelMetadata对象,则具体的属性类型作为它的ModelType属性。属性IsComplexType和IsNullableValueType分别表示以ModelType属性表示的Model类型是一个复杂类型和可空值类型。
在这里判断某个类型是否是复杂类型的条件只有一个,即是否允许字符串类型向该类型的转换。具体来说,将通过ModelType属性表示的Model类型作为传输传入TypeDescriptor的静态方法GetConverter得到一个TypeConverter对象,如果TypeConverter不支持从字符串类型的转换则认为是复杂类型。所以所有的基元类型(Primative Type)均不是复杂类型,所有可空值类型(Nuallable Type)均不是是复杂类型。对于一个默认为复杂类型的自定义的数据类型,我们可以通过TypeConverterAttribute特性标注一个支持从字符串类型转换的TypeConverter使之转变成非复杂类型。
如下面的代码片断所示,我们定义了一个表示二维坐标的Point类型,由于我们在该类型上应用了一个TypeConverterAttribute特性指定了类型为PointTypeConverter的TypeConverter。由于PointTypeConverter支持从字符串到Point类型之间的转换,所以Point并不是一个复杂类型。
1: [TypeConverter(typeof(PointTypeConverter))]
2: public class Point
3: {
4: public double X { get; set; }
5: public double Y { get; set; }
6: public Point(double x, double y)
7: {
8: this.X = x;
9: this.Y = y;
10: }
11: public static Point Parse(string point)
12: {
13: string[] split = point.Split(',');
14: if(split.Length != 2)
15: {
16: throw new FormatException("Invalid point expression.");
17: }
18: double x;
19: double y;
20: if (!double.TryParse(split[0], out x) ||
21: !double.TryParse(split[1], out y))
22: {
23: throw new FormatException("Invalid point expression.");
24: }
25: return new Point(x, y);
26: }
27: }
28: public class PointTypeConverter : TypeConverter
29: {
30: public override bool CanConvertFrom(ITypeDescriptorContext context,
31: Type sourceType)
32: {
33: return sourceType == typeof(string);
34: }
35:
36: public override object ConvertFrom(ITypeDescriptorContext context,
37: CultureInfo culture, object value)
38: {
39: if (value is string)
40: {
41: return Point.Parse(value as string);
42: }
43: return base.ConvertFrom(context, culture, value);
44: }
45: }
通过上面的介绍我们知道表示Model元数据的ModelMetadata具有一个树形的层次结构,某个节点可以看成了其子节点的容器,而ContainerType这是表述容器的类型。同样以前面定义的Contact类型为例,基于该类型本身的ModelMetadata是整个层次树的根节点,所以ContainerType返回Null;基于属性Address的ModelMetadata的ContainerType属性返回Contact类型;而基于Address的属性Province的ModelMetadata的ContainerType属性值则是Address类型。
ModelMetadata的Model属性代表的是作为Model的对象。如果将上面定义的Contact对象作为View的Model,那么表示该Model本身元数据的ModelMetadata对象来说,其Model属性就是该Contact对象;对于基于Contact某个属性的ModelMetadata对象则将对应的属性值作为自己的Model。值得一提的是,该属性是可读可写的,意味着我们可以随时根据需要改变它。另一个属性PropertyName表示对应的属性值,对于根节点ModelMetadata来说,该属性总是返回Null。
ModelMetadata的AdditionalValues属性返回一个字典对象,用于存储一些自定义的属性,字典元素的Key和Value分别代表自定义属性的名称和值。对于自定义属性的添加,我们可以在数据类型或者其数据成员上应用AdditionalMetadataAttribute特性来实现。如下面的代码片断所示,AdditionalMetadataAttribute具有Name和Value两个只读属性分别表示自定义属性的名称和值,它们直接通过构造函数进行初始化。AdditionalMetadataAttribute实现了IMetadataAware接口,对于Model元数据的定制来说,这是一个非常重要并且实用的接口,我们将在下篇对其进行单独介绍。
1: [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Property |
2: AttributeTargets.Class, AllowMultiple=true)]
3: public sealed class AdditionalMetadataAttribute : Attribute, IMetadataAware
4: {
5: public AdditionalMetadataAttribute(string name, object value);
6: public void OnMetadataCreated(ModelMetadata metadata);
7: public string Name { get; }
8: public object Value { get;}
9: }
ModelMetadata的属性Provider是一个ModelMetadataProvider对象,顾名思义,ModelMetadataProvider是ModelProvider的提供者。ModelProvider是ASP.NET MVC整个Model元数据系统的核心,我们将在后续的博文中对其进行单独讲述。
ASP.NET MVC Model元数据及其定制: 初识Model元数据
ASP.NET MVC Model元数据及其定制: Model元数据的定制
ASP.NET MVC Model元数据及其定制:一个重要的接口IMetadataAware
MVC之Model元数据的更多相关文章
- MVC中Model元数据及绑定机制
ASP.NET MVC的Model为View Model,表示最终呈现在View上的数据,而Model元数据的一个重要的作用在于控制对象在View上的呈现方式.说得更加具体点,就是基于某种数据类型的M ...
- ASP.NET MVC Model元数据(五)
ASP.NET MVC Model元数据(五) 前言 在上一篇中我们描述了应用于Model上面的各种用于显示控制的特性类,在本篇中将详细的介绍这些特性类的应用,虽然它们跟Model元数据的直接关系并不 ...
- ASP.NET MVC Model元数据(四)
ASP.NET MVC Model元数据(四) 前言 前面的篇幅讲解了Model元数据生成的过程,并没有对Model元数据生成过程的内部和Model元数据结构的详细解释.看完本篇后将会对Model元数 ...
- ASP.NET MVC Model元数据(三)
ASP.NET MVC Model元数据(三) 前言 在上篇中我们大概的讲解了Model元数据的生成过程,并没有对Model元数据本身和详细的生成过程有所描述,本篇将会对详细的生成过程进行讲解,并且会 ...
- ASP.NET MVC Model元数据(二)
ASP.NET MVC Model元数据(二) 前言 在上篇中,给大家留个对Model元数据的印象,并没有对Model元数据有过多的讲解,而在本篇中也不会对Model元数据的本身来解释,而是针对于它的 ...
- ASP.NET MVC Model元数据(一)
ASP.NET MVC Model元数据(一) 前言 在我初学的时候对Model元数据的概念很模糊,或者说是在大脑中没有它的一个模型,作为小白的我去看网上的一些文章还是两眼一黑啥都看不明白,然后我想退 ...
- 【笔记】ASP.NET MVC Model元数据
问题1:什么叫Model元数据? Model元数据,是针对数据类型的一种描述信息.由于复杂类型(或者说类型嵌套的存在,比如CustomerModel中有一个属性为复杂类型Address)的存在,因此M ...
- mvc Model元数据【学习笔记】
页面中Html.Editorfor(model=>model.fieldname)这些方法,都是通过Model的元数据来生成html的,我们如果想控制最终生成的html,可以通过修改元数据来实现 ...
- ASP.NET MVC Model元数据
ASP.NET MVC Model元数据(三) 前言 在上篇中我们大概的讲解了Model元数据的生成过程,并没有对Model元数据本身和详细的生成过程有所描述,本篇将会对详细的生成过程进行讲解,并且会 ...
随机推荐
- github .net core
https://github.com/dotnet/core/blob/master/Documentation/core-repos.md
- postgresql定位分析消耗CPU高的SQL语句
第一步:使用TOP命令查看占用CPU高的postgresql进程,并获取该进程的ID号,如图该id号为3640 第二步:切换到postgres用户,并且psql连接到数据库,执行如下查询语句 SELE ...
- Python基础知识(五)
# -*- coding: utf-8 -*-# @Time : 2018-12-25 19:31# @Author : 三斤春药# @Email : zhou_wanchun@qq.com# @Fi ...
- javascript将list转换成树状结构
/** * 将list装换成tree * @param {Object} myId 数据主键id * @param {Object} pId 数据关联的父级id * @param {Object} l ...
- 猴哥来了-游戏开发记录17-微信排行榜bug
上线后排行榜bug 1.排序算法 const dataSorter = (gameDatas, field = Consts.OpenDataKeys.LevelKey) => { let d ...
- 有关于Integer的一些小问题
先看一小段源码: Integer a1=; Integer a2=; Integer b1=); Integer b2=); Integer c1=; Integer c2=; System.out. ...
- (转)C# WebApi 异常处理解决方案
原文地址:http://www.cnblogs.com/landeanfen/p/5363846.html 一.使用异常筛选器捕获所有异常 我们知道,一般情况下,WebApi作为服务使用,每次客户端发 ...
- spark2.3.0 配置spark sql 操作hive
spark可以通过读取hive的元数据来兼容hive,读取hive的表数据,然后在spark引擎中进行sql统计分析,从而,通过spark sql与hive结合实现数据分析将成为一种最佳实践.配置步骤 ...
- 阿里云发送短信验证码php_SDK
1.登录阿里云账号下载——aliyun-dysms-php-sdk(我使用的php版本) 下载地址:https://help.aliyun.com/document_detail/55359.html ...
- centos7安装mysql 8
1.查看已有mysql(包括mariadb) rpm -pa | grep mariadb yum list installed | grep mariadb rpm -pa | grep mysql ...