前面的篇幅讲解了Model元数据生成的过程,并无对Model元数据生成过程的内部和Model元数据结构的详细解释。看完本篇后将会对Model元数据有更清楚的了解,固然了也不会是特别全面的,由于后面还有篇幅。但愿能给你们带来好的效果。c#
什么是Model元数据?数据结构
生成Model元数据的过程【一】框架
生成Model元数据的过程【二】ide
ModelMetaData的定义、详解函数
Model元数据应用(经常使用特性应用)-1学习
Model元数据应用(自定义视图模板)-2ui
Model元数据应用(IMetadataAware接口使用)-3this
对于Model元数据的生成能否咱们本身来定义呢?回答是确定的,必须能够阿。MVC框架给咱们提供了顶层基类,在调用的时候是从当前上下文中获取到系统默认实现类(或者是咱们自定义的实现类)。咱们来看一下示例代码1-1.spa
代码1-1调试
public class MyCustomModelMetadataProvider:DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { DataAnnotationsModelMetadata result = new DataAnnotationsModelMetadata(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute); return result; } }
代码1-1中的MyCustomModelMetadataProvider类型继承自DataAnnotationsModelMetadataProvider类型,而且重写了CreateMetadata()的方法,在CreateMetadata()方法中会根据参数attributes中的特性信息来对Model元数据各类属性来操做赋值。这个会在下面说到,代码1-1中并无对attributes参数这些来进行解析,而只是实例化了一个Model元数据类型(DataAnnotationsModelMetadata继承自ModelMetadata)用来返回。这样定义好了事后系统并不会调用咱们自定义的实现,而是须要在项目启动的时候就添加到系统上下文中,咱们就在Global.asax文件中的MvcApplication类型里的Application_Start()方法中来添加示例代码1-2.
代码1-2
ModelMetadataProviders.Current = new MyCustomModelMetadataProvider();
这样定义事后,系统框架在执行的时候就会调用咱们的自定义实现了,还可使用前面篇幅的示例来直接运行,什么结果我没试过不过确定是不会有什么特殊效果,真正的目的不在这,而是在CreateMetadata()方法的入口处设上断点(图1)而后咱们再次按F5执行程序,程序又会执行到咱们自定义实现的CreateMetadata()方法。
图1
按照上面作的意义何在呢?这样作的意义在于在每次断点进来的时候,咱们能够打开调试的即时窗口,而且输入CreateMetadata()方法参数的modelType来查看当前所要生成的Model元数据对应的类型或者是属性,也便于咱们本身去更深刻的学习。还有一个意思就是证实了我上篇所说的那样生成的过程。
下面咱们来看一下系统默认提供的DataAnnotationsModelMetadataProvider类型中是怎么对Model元数据进行操做的,先看一下默认的实现代码,
代码1-3
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { List<Attribute> attributeList = new List<Attribute>(attributes); DisplayColumnAttribute displayColumnAttribute = attributeList.OfType<DisplayColumnAttribute>().FirstOrDefault(); DataAnnotationsModelMetadata result = new DataAnnotationsModelMetadata(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute); // Do [HiddenInput] before [UIHint], so you can override the template hint HiddenInputAttribute hiddenInputAttribute = attributeList.OfType<HiddenInputAttribute>().FirstOrDefault(); if (hiddenInputAttribute != null) { result.TemplateHint = "HiddenInput"; result.HideSurroundingHtml = !hiddenInputAttribute.DisplayValue; } // We prefer [UIHint("...", PresentationLayer = "MVC")] but will fall back to [UIHint("...")] IEnumerable<UIHintAttribute> uiHintAttributes = attributeList.OfType<UIHintAttribute>(); UIHintAttribute uiHintAttribute = uiHintAttributes.FirstOrDefault(a => String.Equals(a.PresentationLayer, "MVC", StringComparison.OrdinalIgnoreCase)) ?? uiHintAttributes.FirstOrDefault(a => String.IsNullOrEmpty(a.PresentationLayer)); if (uiHintAttribute != null) { result.TemplateHint = uiHintAttribute.UIHint; } EditableAttribute editable = attributes.OfType<EditableAttribute>().FirstOrDefault(); if (editable != null) { result.IsReadOnly = !editable.AllowEdit; } else { ReadOnlyAttribute readOnlyAttribute = attributeList.OfType<ReadOnlyAttribute>().FirstOrDefault(); if (readOnlyAttribute != null) { result.IsReadOnly = readOnlyAttribute.IsReadOnly; } } DataTypeAttribute dataTypeAttribute = attributeList.OfType<DataTypeAttribute>().FirstOrDefault(); DisplayFormatAttribute displayFormatAttribute = attributeList.OfType<DisplayFormatAttribute>().FirstOrDefault(); // SetFromDataTypeAndDisplayAttributes(result, dataTypeAttribute, displayFormatAttribute); ScaffoldColumnAttribute scaffoldColumnAttribute = attributeList.OfType<ScaffoldColumnAttribute>().FirstOrDefault(); if (scaffoldColumnAttribute != null) { result.ShowForDisplay = result.ShowForEdit = scaffoldColumnAttribute.Scaffold; } DisplayAttribute display = attributes.OfType<DisplayAttribute>().FirstOrDefault(); string name = null; if (display != null) { result.Description = display.GetDescription(); result.ShortDisplayName = display.GetShortName(); result.Watermark = display.GetPrompt(); result.Order = display.GetOrder() ?? ModelMetadata.DefaultOrder; name = display.GetName(); } if (name != null) { result.DisplayName = name; } else { DisplayNameAttribute displayNameAttribute = attributeList.OfType<DisplayNameAttribute>().FirstOrDefault(); if (displayNameAttribute != null) { result.DisplayName = displayNameAttribute.DisplayName; } } RequiredAttribute requiredAttribute = attributeList.OfType<RequiredAttribute>().FirstOrDefault(); if (requiredAttribute != null) { result.IsRequired = true; } return result; }
在代码1-3中,咱们看到首先会根据参数attributes转换为Attribute集合类型的attributeList变量,而后就是在此集合中搜寻第一个DisplayColumnAttribute类型的特性,暂且先不说这个特性类型是干什么的,由于我如今也不太明白。
而后就是根据CreateMetadata()方法中的参数实例化一个DataAnnotationsModelMetadata类型的元数据,这个类型上面说过了。继续往下看,而后就到了从attributeList变量获取第一个HiddenInputAttribute类型的特性实例,在判断不为空后,对Model元数据DataAnnotationsModelMetadata类型变量result的两个属性开始赋值(下文中对Model元数据DataAnnotationsModelMetadata类型变量result统称叫result),首先第一个是Model元数据的TemplateHint属性,这个属性表示着这个Model元数据所表示的对象要使用哪一个视图模板来生成Html代码(视图模板的内容这个系列的后面篇幅会有讲解,到时候再回头来看一下,学习嘛感受就是一个迭代的过程)。而后是HideSurroundingHtml属性的赋值,对应的是HiddenInputAttribute类型的DisplayValue值,HiddenInputAttribute类型表示的是是否将属性或者字段值显示为隐藏的Input元素,若是咱们这样写的话[HiddenInput(DisplayValue = false)],HideSurroundingHtml属性值则为true,表明的意思就是使用关联的Html元素来呈现对象模型,意思就是用HiddenInputAttribute类型所关联隐藏输入域来呈现咱们所指定的属性或者字段。这里可能有点绕,不过不妨碍,下个篇幅会讲示例用的效果。
切回主题继续讲,下面则是从attributeList中获取UIHintAttribute类型的集合,而且通过一番判断获取一个UIHintAttribute类型的实例,而且仍是赋值到TemplateHint属性(上面说过),这里就覆盖掉了,在咱们使用默认的Model元数据提供程序的时候就要注意这些了,再继续往下看。
从attributeList中获取第一个EditableAttribute类型的实例,而且根据EditableAttribute类型实例中的AllowEdit属性值来设置result的IsReadOnly属性值,表明着指示这个模型是否只读,EditableAttribute类型指示模型是否可编辑的意思和下面的ReadOnlyAttribute类型很像,只不过一样是实现只读效果两个类型使用中设置的属性值是相反的。
一样是从attributeList获取符合类型条件的第一个DataTypeAttribute类型实例,还有个是DisplayFormatAttribute类型实例,这里会调用默认的提供程序里的另外一个函数,在此就不作多的介绍了,我就稍微的说一下就好了。为何把这两个放一块儿呢?由于他们都是对指定的模型输出格式的设置有关。
ScaffoldColumnAttribute类型实例表示着是否使用基架(模板视图辅助器的一种,EditorForModel属于其中之一),当某项属性上使用了这个特性类的时候,在使用基架的时候会直接跳过这项属性,在生成的页面中也不会发现这项属性。(遭到了嫌弃)
一样的DisplayAttribute类型的实例也是从attributeList获取符合类型条件的第一个,DisplayAttribute类型实例里有个Name属性会被设置到result的DisplayName属性,这个属性的意思就是指定的模型显示到页面的值。而DisplayNameAttribute类型实例的意思和DisplayAttribute类型的相近,只不过DisplayNameAttribute类型能够用于类类型,转定义咱们一看便知。
最后对于RequiredAttribute类型实例的意思会在Model验证篇幅中说明。