前面的篇幅講解了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驗證篇幅中說明。