Model元数据解析
- Model 元数据是针对数据类型的一种描述信息,主要用于控制数据类型本身及其成员属性在界面上的呈现方式,同时也为Model 绑定和验证提供必不可少的元数据信息。一个复杂数据类型通过属性的方式定义了一系列的数据成员,而Model 元数据不仅仅是数据类型本身的描述,对数据成员的描述也包含其中,所以Model 元数据具有一个层次化结构。
- AdditionalMetadataAttribute定义的值放在元数据的AdditionalValues中.实现接口IMetadat aAware
- ModelMetadata包含几个重要的属性ModelType,IsComplexType,IsNullableValueType,ContainerType,PropertyName,Model
- IsComplexType
判断某个类型是否是复杂类型的条件只有一个,即是否允许字符串类型向该类型的转换。所以所有的基元类型C Primative Type) 和可空值类型CNullable Type) 均不是复杂类型。对于一个默认为复杂类型的自定义数据类型,可以在它上面应用TypeConverterAttribute 特性并指定一个支持字符串转换的TypeConverter 类型,使之转变成简单类型。
- UIHintAttribute
ModelMetadata 的TemplateHint 属性可以通过UIHintAttribute 特性来定制.指定要使用的模板. (UIHintImplementation).多个的时候,要区分优先级.
- HiddenInputAttribute
HiddenInputAttribute 特性Mode lMetadata 对象的TemplateHint 属性设置为"HiddenI nput",其HideSurroundingHtrnl 属性则对应着HiddenInpu tAttribute 的DisplayValue 属性。UIHintAttribute 具有更高的优先级。
- ScaffoldColumnAttribute
具有一个布尔类型的只读属性Scaffold表示目标元素是否应该存在于呈现在最终生成的HTML 的基架中,HiddenInputAttribute 是隐藏的,但是包含在HTML中.
ScaffoldColumnAttribute 最终用于控制ModelMetadata 的ShowForDisplay 和ShowForEdit属性
- Data TypeAttribute控制显示格式,如Email.DisplayFormatAttribute
其中DataTypeAttribute 中设置的数据类型对应于ModelMetadata 的DataTypeName
属性,而DisplayF ormatAttribute 的ConvertEmp可S位ingToNull 和NullDisplayText 属性对应着ModelMetadata 的同名属性。DataTypeAttribute可以继承自定义格式
- EditableAttribute ReadOnlyAttribute
- Display DisplayNameAttribute
- RequiredAttribute
- IMetadataAware,通过实现此接口可以实现对元数据的定制,接口中只有一个方法 void OnMetadataCreated(ModelMetadata metadata);
- 当Model 元数据被创建出来后,上述的这一系列数据注解特性会被提取出来对其进行初始化,然后获取应用在目标元素上所有实现了IMetadataAware 接口的特性,并将初始化的ModelMetadata 对象作为参数调用OnMetadataCreated 方法。通过自定义实现该接口的特性不仅仅可以添加一些额外的元数据属性,还可以修改己经通过相应的标注特性初始化的相关属性。
- ModelMetadata 的RequestValidationEnabled 属性开启/关闭请求验证的开关。该属性在默认情况下为True ,意味着默认开启针对HTI\在L 标记的请求验证。
- AllowHtmlAttribute metadata.RequestValidationEnabled = false;
- 自定义的控件模板要放在Share下或者Controller对应的View中的EditorTemplates文件夹下.可以理解为,一般的页面也是对应的Model的模板页,两者之间的实现思路是一样的.
@model bool <table> <tr> <td>@Html.RadioButton("", true, Model)</td> <td>@Html.RadioButton("", false, Model)</td> </tr> </table>
- MVC内部定义了很多模板,当调用HtmlHelper.For的时候,会根据显示模式或者编辑模式和元数据找到一个显示用的模板.
- MVC预定义的模板:EmailAddress,Hiddenlnput,HTML,(Text,String效果一样),URL,MultilineText,Password,decimal,Boolean,Collection,Object
- 模板查找路径顺序
- 传入的TemplateName
- ModelMetadata的TemplateHint属性
- DataTypeName属性
- 非空类型(不是Nullable类型)用类型名作为模板View名称Nullable类型的获取真实类型作为View名称,如Int?的模板为Int32的View
- 非复杂类型,会选择String的模板,然后退出检索,否则下一步
- 如果声明是接口,
- 如果继承IEnumerable采用Collection模板
- 选择Object模板
- ,然后退出检索,否则下一步
- 向父类追溯,选择各级父类的名称作为模板
- 直到Object类型,如果实现Ienumerable,则选择Collection代替Object.
- 查找的源代码
internal static IEnumerable<string> GetViewNames(ModelMetadata metadata, params string[] templateHints) { foreach (string templateHint in templateHints.Where(s => !String.IsNullOrEmpty(s))) { yield return templateHint; } // We don't want to search for Nullable<T>, we want to search for T (which should handle both T and Nullable<T>) Type fieldType = Nullable.GetUnderlyingType(metadata.RealModelType) ?? metadata.RealModelType; // TODO: Make better string names for generic types yield return fieldType.Name; if (!metadata.IsComplexType) { yield return "String"; } else if (fieldType.IsInterface) { if (typeof(IEnumerable).IsAssignableFrom(fieldType)) { yield return "Collection"; } yield return "Object"; } else { bool isEnumerable = typeof(IEnumerable).IsAssignableFrom(fieldType); while (true) { fieldType = fieldType.BaseType; if (fieldType == null) { break; } if (isEnumerable && fieldType == typeof(Object)) { yield return "Collection"; } yield return fieldType.Name; } } }
- 优先使用自定义的模板,路径是"DisplayTemplate/{TemplateName}"和"EditorTemplate/{TemplateName}",找不到的时候在默认的模板中查找
- 系统默认的模板
- 编辑系统模板
private 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 }, { "PhoneNumber", DefaultEditorTemplates.PhoneNumberInputTemplate }, { "Url", DefaultEditorTemplates.UrlInputTemplate }, { "EmailAddress", DefaultEditorTemplates.EmailAddressInputTemplate }, { "DateTime", DefaultEditorTemplates.DateTimeInputTemplate }, { "Date", DefaultEditorTemplates.DateInputTemplate }, { "Time", DefaultEditorTemplates.TimeInputTemplate }, { typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(sbyte).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(int).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(uint).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(long).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(ulong).Name, DefaultEditorTemplates.NumberInputTemplate }, { typeof(bool).Name, DefaultEditorTemplates.BooleanTemplate }, { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate }, { typeof(string).Name, DefaultEditorTemplates.StringTemplate }, { typeof(object).Name, DefaultEditorTemplates.ObjectTemplate }, };
- 编辑系统模板
- 显示系统模板
private 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 }, };
- 自定义模板查找路径
private static readonly Dictionary<DataBoundControlMode, string> _modeViewPaths = new Dictionary<DataBoundControlMode, string> { { DataBoundControlMode.ReadOnly, "DisplayTemplates" }, { DataBoundControlMode.Edit, "EditorTemplates" } };
- 一个类型的值为null时,不能调用GetType()方法.没有实体,这是个实例方法
- Nullable类型调用GetType时,返回值是真实的类型T.
- Type.IsGenericTypeDefinition获取泛型类型是否是泛型定义,还是具体的泛型.GetGenericTypeDefinition获取泛型对应的定义.GetGenericArguments获取泛型的泛型参数类型
- ModelMetadataProvider元数据提供机制
- DataAnnotationsModelMetadata
- System.Web.Mvc.CachedDataAnnotationsModelMetadata 才是默认使用的ModelMetadata 类型
- Provider创建ModelMetadata的过程,创建属性的modelmetadata.先找特性,创建modelmetadata,应用IMetadataAware的特性,因此他的优先级更高.
protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) { IEnumerable<Attribute> attributes = FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>()); ModelMetadata result = CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name); ApplyMetadataAwareAttributes(attributes, result); return result; }
- ModelMetadataProviders 具有一个ModelMetadataProvider类型 的静态可读可写属性Current 用于获取和设置当前使用的ModelMetadataP rovider 。在默认情况下, Current 属性返回的就是一个CachedDataAnnotationsModelMetadataProvider 对象。
- ControllerBase中有一个ViewData对象,用于向View传递数据.ViewData中有一个ModelMetadata元数据对象
- ASPNETMVC 具有一个可扩展的以Mode lM etadataProvider 为核心的Model 元数据提供系统,默认采用的ModelMetadataProvider 类型为CachedDataAnnotationsModelMetadataProvider.
- System.Web.Mvc.CachedDataAnnotationsModelMetadata 才是默认使用的ModelMetadata 类型