1. 程式人生 > 其它 >ASP.NET MVC Model元資料及其定製: Model元資料的定製

ASP.NET MVC Model元資料及其定製: Model元資料的定製

在《上篇》我們已經提到過了,Model元資料的定製是通過在作為Model的資料型別極其屬性成員上應用相應的特性來實現,這些用於宣告式元資料定義的特性大都定義在System.ComponentModel.DataAnnotations.dll程式集中,程式集的名稱同時也是對應的名稱空間名稱,所以我們可以它們為資料註解特性(Data Annotation Attribute),接下來我們來介紹一些常用的資料註解特性,以及它們對於元資料具有怎樣的影響。[本文已經同步到《How ASP.NET MVC Works?》中]

目錄

一、UIHintAttribute

二、HiddenInputAttribute與ScaffoldColumnAttribute

三、DataTypeAttribute與DisplayFormatAttribute

四、EditableAttribute與ReadOnlyAttribute

五、DisplayAttribute與DisplayNameAttribute

六、RequiredAttribute

一、UIHintAttribute HtmlHelper和HtmlHelper<TModel>定義了一系列的基於Model的模板方法,比如Display/DisplayFor、Editor/EditorFor、DisplayForModel/EditForModel、Lable/LabelFor和DisplayText/DisplayTextFor。所謂模板方法,就是說我們在通過呼叫這些方法將代表Model的資料呈現在View中的時候,並不對最終呈現的UI元素進行顯失地控制,而採用預設或者指定的模板來決定最終呈現在瀏覽器中的HTML。每個具體的模板均具有相應的名稱,這些模板方法在進行Model呈現的時候根據對應的Model元資料得到對應的模板名稱。具體來說,模板的名稱通過ModelMetadata的TemplateHint屬性表示,如下面的程式碼片斷所示,這是一個字串型別的可讀寫屬性。

 1: public class ModelMetadata         
 2: {         
 3:     //其他成員        
 4:     public virtual string TemplateHint{get;set;}         
 5: }    

ModelMetadata的TemplateHint屬性可以通過UIHintAttribute特性來定製。如下面的程式碼片斷所示,UIHintAttribute具有PresentationLayer和UIHint兩個只讀屬性,分別用於限制展現層的型別(比如“HTML”、“Silverlight”、“WPF”、“WinForms”等和模板名稱,這兩個屬性均在建構函式中初始化。

 1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=true)]        
 2: public class UIHintAttribute : Attribute         
 3: {       
 4:     //其他成員        
 5:     public UIHintAttribute(string uiHint);          
 6:     public UIHintAttribute(string uiHint, string presentationLayer);      
 7:             
 8:     public string PresentationLayer { get; }        
 9:     public string UIHint { get; }         
10: }    

通過應用在UIHintAttribute上的AttributeUsageAttribute定義我們不難看出,由於其AllowMultiple屬性被設定為True,意味著我們可以在相同的目標元素上應用多個UIHintAttribute特性,那麼哪一個會被選擇用於定製Model元資料呢? 如果多個UIHintAttribute應用到了相應的元素(型別或者屬性),會先選擇一個PresentationLayer屬性為“MVC”(不區分大小寫)的UIHintAttribute。如果這樣的UIHintAttribute不存在,則選擇一個PresentationLayer屬性值為空的UIHintAttribute。值得一提的是,如果具有多個匹配的UIHintAttribute可控選擇,系統會選擇第一個,但是通過反射獲取到的Attribute的順序和Attribute被標註的屬性沒有直接的關係。 接下來我們通過一個簡單的例項來演示UIHintAttribute特性對Model元資料的影響,以及對應用在相同目標元素上的多個UIHintAttribute的選擇策略。考慮到重用性,我們編寫了如下一個靜態輔助方法GetModelMetadata<TModel>用於獲取Model型別為TModel針對指定屬性的Model元資料。

 1: public static ModelMetadata GetModelMetadata<TModel>(string propertyName)         
 2: {        
  3:     ModelMetadataProvider provider = ModelMetadataProviders.Current;         
 4:     ModelMetadata containerMetadata =  new ModelMetadata(provider, null, () => null, typeof(TModel), null);         
 5:     return containerMetadata.Properties.FirstOrDefault(m => m.PropertyName == propertyName);         
 6: }   

我們通過如下的程式碼定義了一個型別為Model的資料型別,三個屬性Foo、Bar和Baz定義其中。對於屬性Bar來說,我們同時應用了兩個模板名稱分別為“Template A”和“Template B”的UIHintAttribute特性,後者將字元“Mvc”作為presentationLayer引數的值。屬性Baz通用應用了基於模板名稱“Template A”的UIHintAttribute特性。

 1: public class Model          
2: {         
 3:     public string Foo { get; set; }         
 4:           
 5:     [UIHint("Template A")]         
 6:     [UIHint("Template B", "Mvc")]         
 7:     public string Bar { get; set; }         
 8:           
 9:     [UIHint("Template A")]        
 10:     public string Baz { get; set; }         
11: }    

現在我們在一個控制檯程式中編寫如下的測試程式。我們通過上面定義的輔助方法GetModelMetadata<TModel>建立針對定義在資料型別Model中的Foo、Bar和Baz三個屬性的ModelMetadata,並分別打印出對應的TemplateHint屬性。

 1: ModelMetadata foo = GetModelMetadata<Model>("Foo");         
 2: ModelMetadata bar = GetModelMetadata<Model>("Bar");          
3: ModelMetadata baz = GetModelMetadata<Model>("Baz");         
 4:           
 5: Console.WriteLine("Foo: {0}", foo.TemplateHint??"N/A");          
6: Console.WriteLine("Bar: {0}", bar.TemplateHint ?? "N/A");         
 7: Console.WriteLine("Baz: {0}", baz.TemplateHint ?? "N/A");    

上面的測試程式執行之後會在控制檯上產生如下的輸出結果,這和我們上面介紹的關於UIHintAttribute特性針對Model元資料的定製,以及針對應用在相同目標元素上的多個UIHintAttribute特性的選擇策略是相符的。

1: Foo: N/A

2: Bar: Template B

3: Baz: Template A

二、HiddenInputAttribute與ScaffoldColumnAttribute 一個作為Model的資料型別往往具有一個唯一標識,當我們以編輯模式將Model物件在View中呈現的時候,往往不允許用於對作為唯一標識的屬性進行修改。如果ID不具有可讀性(比如是一個隨機數或者GUID),有時候甚至不希望讓它顯示在介面上。這個時候我們就會使用到特性HiddenInputAttribute。 HiddenInputAttribute並沒有定義在System.ComponentModel.DataAnnotations名稱空間下,它的名稱空間為System.Web.Mvc,所以該特使是專門為ASP.NET MVC設計的。顧名思義,HiddenInputAttribute會將目標物件以型別為hidden的<input/>元素呈現出來。在預設的情況下,應用了HiddenInputAttribute特性的目標物件依然會以只讀的形式顯示出來。如果不希望顯示,可以將如下所示的布林型別的DisplayValue設定為False(預設值為False)。

1: [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple=false, Inherited=true)]          
2: public sealed class HiddenInputAttribute : Attribute          
3: {         
4:     public HiddenInputAttribute();          
5:     public bool DisplayValue { get;set; }          
6: }    

同樣以前面定義的Model型別為例,現在我們將HiddenInputAttribute特性應用在屬性Foo和Bar上,後者將DisplayValue設定為False。

1: public class Model          
2: {          
3:     [HiddenInput]          
4:     public string Foo { get; set; }          
5:            
6:     [HiddenInput(DisplayValue = false)]          
7:     public string Bar { get; set; }         
8:            
9:     public string Baz { get; set; }        
10: }    

現在我們通過呼叫HtmlHelper<TModel>的擴充套件方法EditForModel方法將一個具體的Model物件(new Model { Foo = "foo", Bar = "bar", Baz = "baz" })顯示在某個基於Model型別的強型別View中。最終呈現出來的效果如下圖所示,我們可以看到針對應用了HiddenInputAttribute的兩個屬性Foo和Bar,前者以只讀的形式顯示出來;後者卻在介面上看不到。 如下所示的用於呈現Foo、Bar和Baz三個屬性對應的HTML,從中我們可以清楚地看到兩個應用了HiddenInputAttribute的屬性,不論其DisplayValue屬性具有怎樣的值,均對應著一個的型別為hidden的<input>元素。

1: <div class="editor-label"><label for="Foo">Foo</label></div>          
2: <div class="editor-field">foo<input id="Foo" name="Foo" type="hidden" value="foo" /> ...          
3: </div>          
4:            
5: <input id="Bar" name="Bar" type="hidden" value="bar" />          
6:            
7: <div class="editor-label"><label for="Baz">Baz</label></div>          
8: <div class="editor-field">         
 9:   <input class="text-box single-line" id="Baz" name="Baz" type="text" value="baz" />...        
10:   </span>         
11: </div>    

HiddenInputAttribute針對Model元資料的定製體現ModelMetadata的如下兩個屬性的上,其中一個就是上面介紹的TemplateHint,另一個則是布林型別的屬性HideSurroundingHtml,表示目標元數是否需要通過相應的HTML呈現在UI介面上。具體來說,針對應用了HiddenInputAttribute的目標元素對應的ModelMetadata物件,其被設定為“HiddenInput”,並將其DisplayValue屬性為HideSurroundingHtml屬性賦值。“HiddenInput”為ASP.NET MVC自身定義的一個預設模板名稱,也就是說當目標元素應用了HiddenInputAttribute特性,這個預設模板別用來實現對其的UI呈現。

1: public class ModelMetadata          
2: {          
3:     //其他成員         
 4:     public virtual string   TemplateHint{get;set;}          
5:     public virtual bool     HideSurroundingHtml { get; set; }          
6: }    

我們同樣通過一個測試程式來驗證HiddenInputAttribute特性對Model元素據的定製。針對上面定義的Model型別(Foo和Bar屬性應用了HiddenInputAttribute特性),我們通過如下的測試程式將基於Foo、Bar和Baz屬性的三個ModelMetadata物件獲取出來,然後分別打印出它們的TemplateHint和HideSurroundingHtml屬性。

1: ModelMetadata foo = GetModelMetadata<Model>("Foo");

2: ModelMetadata bar = GetModelMetadata<Model>("Bar");

3: ModelMetadata baz = GetModelMetadata<Model>("Baz");

4: 

5: Console.WriteLine("{0,-5}{1, -14}{2, -20}", "", "TemplateHint", "HideSurroundingHtml");

6: Console.WriteLine(new string('-', 40));

7: 

8: Console.WriteLine("{0,-5}{1, -14}{2, -20}", "Foo", foo.TemplateHint ?? "N/A", foo.HideSurroundingHtml);

9: Console.WriteLine("{0,-5}{1, -14}{2, -20}", "Bar", bar.TemplateHint ?? "N/A", bar.HideSurroundingHtml);

10: Console.WriteLine("{0,-5}{1, -14}{2, -20}", "Baz", baz.TemplateHint ?? "N/A", baz.HideSurroundingHtml);

上面的程式執行之後會在控制檯上產生如下的輸出結果,這和我們前面的介紹是相匹配的。

1:  TemplateHint HideSurroundingHtml

2: ----------------------------------------

3: Foo HiddenInput False

4: Bar HiddenInput True

5: Baz N/A False

有的讀者可能會問這樣一個問題,UIHintAttribute和HiddenInputAttribute都會設定表示Model元資料的ModelMetadata物件的TemplateHint屬性,如果兩個特性均應用到相同的目標元素上,最終生成的ModelMetadata物件具有怎樣的TemplateHint屬性值呢?答案是:UIHintAttribute具有更高的優先順序。 對於應用了HiddenInputAttribute特性目標元素,不論其DisplayValue具有怎樣的值,都會出現在通過模板方法生成的HTML中,如果我們希望將它從HTML中移除,我們可以應用另一個叫作ScaffoldColumnAttribute的特性。我們將通過預定義模板自動生成HTML的方式成為“基架(Scaffolding)”,ScaffoldColumnAttribute中的ScaffoldColumn代表存在於“基架”中並最終呈現在HTML中的字典,而該特性本身則用於控制目標元素是否應該存在於基架之中。如下面的程式碼片斷所示,ScaffoldColumnAttribute具有一個布林型別的只讀屬性Scaffold表示目標元素是否應該存在於呈現在最終生成的HTML的基架中,該屬性在建構函式中初始化。

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false)]          
2: public class ScaffoldColumnAttribute : Attribute         
 3: {            
4:     public ScaffoldColumnAttribute(bool scaffold);          
5:     public bool Scaffold { get; }          
6: }    ScaffoldColumnAttribute

最終用於控制用於表示針對目標物件的ModelMetadata物件的ShowForDisplay和ShowForEdit屬性。如下面的程式碼所示,這是兩個布林型別的屬性,分別表示目標元素是否應該出現在顯示和編輯模式的基架中。如果ShowForDisplay的屬性為False,在呼叫模板方法EditorFor/EditorForModel方法時目標元素將不會出現在最終生成的HTML中;同理,在通過DisplayFor/DisplayForModel方法生成的HTML將不會包含ShowForDisplay為False的元素。這兩個屬性值在預設情況下均為True。

1: public class ModelMetadata          
2: {          
3:     //其他成員         
 4:     public virtual bool ShowForDisplay { get; set; }          
5:     public virtual bool ShowForEdit { get; set; }          
6: }      

三、DataTypeAttribute與DisplayFormatAttribute 用於指定資料型別的DataTypeAttribute特性是我們經常使用的資料標註特性。這裡所說的資料型別不是我們理解的CLR型別,而是通過DataType列舉表示的具有某種顯示格式的資料型別。如下面的程式碼片斷所示,DataType列舉定義了一系列包括時間、日期、電話號碼、貨幣、Html、電子郵箱地址在內的資料型別。

 1: public enum DataType       
 2: {        
 3:     Custom,     
 4:     DateTime,         
 5:     Date,          
 6:     Time,           
 7:     Duration,          
 8:     PhoneNumber,         
 9:     Currency,         
10:     Text,         
11:     Html,         
12:     MultilineText,         
13:     EmailAddress,         
14:     Password,         
15:     Url,         
16:     ImageUrl,        
17:     CreditCard,         
18:     PostalCode,         
19:     Upload         
20: }    

為Model元資料設定資料型別的DataTypeAttribute實際上是一個驗證特性。如下面的程式碼片斷所示,DataTypeAttribute直接繼承自ValidationAttribute,關於驗證和驗證特性,我們會在後續的博文進行單獨討論。除了具有一個DataType列舉型別的DataType只讀屬性之外,DataTypeAttribute還具有一個字串型別的表示自定義資料型別的CustomDataType屬性,它們均在相應的建構函式中初始化。

1: [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple=false)]         
 2: public class DataTypeAttribute : ValidationAttribute         
 3: {          
4:     public DataTypeAttribute(DataType dataType);         
 5:     public DataTypeAttribute(string customDataType);        
 6:             
 7:     public virtual string GetDataTypeName();         
8:     public override bool IsValid(object value);          
9:           
10:     public string CustomDataType { get; }        
11:     public DataType DataType { get;  }         
12:     public DisplayFormatAttribute DisplayFormat { get; }         
13: }    

DataTypeAttribute的只讀屬性DisplayFormat涉及到另一個用於進行格式化的DisplayFormatAttribute特性,它的主要目的在於指定一個格式化字串以控制資料在UI介面上的顯示格式。如下面的程式碼片斷所示,格式化字串通過屬性DataFormatString表示,布林型別的屬性ApplyFormatInEditMode和HtmlEncode表示格式化規則是否需要應用到編輯模式,以及是否需要對目標內容實施HTML編碼, 預設情況下這兩個屬性值分別為False和True。DisplayFormatAttribute的屬性NullDisplayText和ConvertEmptyStringToNull與空值/空字串的處理有關,前者表示針對空值(Null)物件的顯示文字,後者表示是否將傳入的空字串轉換成Null。

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false)]          2: public class DisplayFormatAttribute : Attribute          
3: {          
4:     public DisplayFormatAttribute();          
5:            
6:     public string DataFormatString { get; set; }          
7:            
8:     public bool ApplyFormatInEditMode { get; set; }              
9:     public bool HtmlEncode { get; set; }         
10:           
11:     public string NullDisplayText { get; set; }        
 12:     public bool ConvertEmptyStringToNull { get; set; }         
13: }    

定義在DataType列舉中的部分資料型別(比如Date、Time、Duration和Currency等)都具有各自的格式。當DataTypeAttribute通過指定的DataType列舉值被建立的時候,會根據對應的格式建立一個DisplayFormatAttribute物件作為其DisplayFormat屬性值。 DataTypeAttribute和DisplayFormatAttribute對Model元資料的定製涉及到ModelMetadata的如下屬性。其中DataTypeAttribute中設定的資料型別對應於ModelMetadata的DataTypeName屬性,而DisplayFormatAttribute的ConvertEmptyStringToNull和NullDisplayText屬性對應著ModelMetadata的同名屬性。通過DisplayFormatAttribute的DataFormatString屬性設定的格式化字串會賦值給ModelMetadata的DisplayFormatString屬性,表示顯示模式下的格式化字串。如果ApplyFormatInEditMode屬性為True,該屬性會賦值給ModelMetadata的EditFormatString屬性,表示編輯模式下的格式化字串。

 1: public class ModelMetadata          
2: {          
3:     //其他成員          
4:     public virtual string   DataTypeName { get; set; }          
5:           
 6:     public virtual string   DisplayFormatString { get; set; }          
7:     public virtual string   EditFormatString { get; set; }          
8:            
9:     public virtual bool     ConvertEmptyStringToNull { get; set; }         
10:     public virtual string   NullDisplayText { get; set; }        
11: }    

ModelMetadata表示資料型別名稱DataTypeName屬性型別為字串,針對定義在DataType列舉中的每個列舉項均對應著一個預定義的字串表示對應的資料型別。如果通過DataTypeAttribute特性已字串的方式指定一個自定義資料型別,該字串直接作為ModelMetadata的DataTypeName屬性值。如果沒有顯示地對資料型別進行設定,並且DisplayFormatAttribute的HtmlEncode屬性為False(不需要隊目標內容進行HTML編碼),生成的ModelMetadata物件的DataTypeName屬性值為Html(相當於DataType.Html). 由於一個DataTypeAttribute對應著一個DisplayFormatAttribute,如果兩個這兩個特性同時應用在了相同的目標元素上,在設定衝突的情況下後者(DisplayFormatAttribute)具有更高的優先順序。

四、EditableAttribute與ReadOnlyAttribute EditableAttribute和ReadonlyAttribute用於控制目標元素的可讀寫性。如下面的程式碼片斷所示,EditableAttribute和ReadonlyAttribute分別具有一個布林型別的屬性AllowEdit和IsReadOnly分別表示是否執行編輯和是否只讀。

1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false, Inherited=true)]          2: public sealed class EditableAttribute : Attribute          
3: {          
4:     //其他成員          
5:     public EditableAttribute(bool allowEdit);         
6:     public bool AllowEdit {  get;  private set; }          
7: }          
8:            
9: [AttributeUsage(AttributeTargets.All)]         
10: public sealed class ReadOnlyAttribute : Attribute         
11: {         
12:     //其他成員         
13:     public ReadOnlyAttribute(bool isReadOnly);         
14:     public bool IsReadOnly {  get; }         
15: }    

不允許編輯即為只讀,所以這兩個標註特性具有相同的作用。它們共同控制著ModelMetadata如下所示的IsReadOnly屬性。如果同時將EditableAttribute和ReadonlyAttribute應用到相同的目標元素上並且作出相反的設定(讓EditableAttribute的AllowEdit屬性和ReadonlyAttribute的IsReadOnly屬性具有相同的布林值),EditableAttribute特性具有更高的優先順序。

1: public class ModelMetadata          
2: {          
3:     //其他成員          
4:     public virtual bool IsReadOnly{get; set;}              
5: }    

我們通過如下的方式將特性EditableAttribute和ReadonlyAttribute同時應用到資料型別Model的Bar和Baz屬性上,並在讀寫性上作出相反的設定。而在屬性Foo應用ReadonlyAttribute特性將其設為只讀。

1: public class Model          
2: {          
3:     [ReadOnly(true)]          
4:     public string Foo { get; set; }          
5:            
6:     [Editable(true)]          
7:     [ReadOnly(true)]          
8:     public string Bar { get; set; }          
9:           
10:     [Editable(false)]         
11:     [ReadOnly(false)]         
12:     public string Baz { get; set; }         
13: }    

然後我們採用如下的程式碼呼叫之前定義的輔助方法GetModelMetadata<TModel>得到針對定義在Model型別中三個屬性的ModelMetadata物件,最終將它們的IsReadOnly打印出來。

1: ModelMetadata foo = GetModelMetadata<Model>("Foo");          
2: ModelMetadata bar = GetModelMetadata<Model>("Bar");          
3: ModelMetadata baz = GetModelMetadata<Model>("Baz");         
4:            
5: Console.WriteLine("Foo: {0}", foo.IsReadOnly);          
6: Console.WriteLine("Bar: {0}", bar.IsReadOnly);          
7: Console.WriteLine("Baz: {0}", baz.IsReadOnly);    

上面的測試程式執行之後會在控制檯上產生如下的輸出結果。由於Foo屬性上僅僅應用了ReadonlyAttribute特性,所以它控制了ModelMetadata的IsReadOnly屬性;而Bar和Baz屬性則同時應用EditableAttribute和ReadonlyAttribute兩個特性,ModelMetadata的IsReadOnly屬性最終通過EditableAttribute特性來控制。

1: Foo: True

2: Bar: False

3: Baz: True

五、DisplayAttribute與DisplayNameAttribute DisplayAttribute特性為目標元素定義一些說明性文字。如下面的程式碼片斷所示,DisplayAttribute具有5個基本屬性,其中Name和ShortName為目標元素設定一個顯示名稱和簡短的顯示名稱。屬性Description和Order為目標元素設定描述性文字和用於排序的權重。字串型別的Prompt屬性為目標元素設定一個字串,它在UI介面上以水印的方式呈現。

1: [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple=false)]          
2: public sealed class DisplayAttribute : Attribute          
3: {          
4:     //其他成員          
5:     public DisplayAttribute();          
6:            
7:     public string GetName();          
8:     public string GetShortName();          
9:     public string GetDescription();         
10:     public int?   GetOrder();         
11:     public string GetPrompt();         
12:              
13:     public string Name { get; set; }         
14:     public string ShortName { get; set; }         
15:     public string Description { get; set; }          
16:     public int    Order { get; set; }            
17:     public string Prompt { get; set; }         
18:              
19:     public Type   ResourceType { get; set; }         
20: }    

由於DisplayAttribute特性設定的文字最終都是面向終端使用者的,所以有必要對其進行本地化(Localization),為此該特性允許我們通過資原始檔的方式來定義它們。DisplayAttribute特性的ResourceType代表採用的資原始檔生成的型別,如果我們對該屬性進行了顯式設定,上述5個屬性值將會被認為是對應的資源條目的名稱。正因為如此,如果我們需要得到最終用於顯示的文字,不能通過相應的屬性,而需要通過相應的GetXxx方法。 另一個定義在名稱空間System.ComponentModel下的DisplayNameAttribute特性則專門用於設定目標元素的顯示名稱,如下面的程式碼片斷所示,目標元素的顯示名稱通過只讀屬性DisplayName表示,該屬性在建構函式中被初始化。如果呼叫預設的建構函式,該屬性會被設定為空字串。

1: [AttributeUsage(AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class)]          
2: public class DisplayNameAttribute : Attribute          
3: {          
4:     public DisplayNameAttribute();          
5:     public DisplayNameAttribute(string displayName);          
6:            
7:     public virtual string DisplayName { get; }          
8: }    

DisplayAttribute和DisplayNameAttribute特性對Model元資料的定製涉及到如下五個屬性。DisplayAttribute的GetName方法的返回值和DisplayNameAttribute的屬性DisplayName對應於ModelMetadata的DisplayName屬性。DisplayAttribute的GetShortName方法對應著ModelMetadata的ShortDisplayName屬性,而GetDescription和GetOrder對應著ModelMetadata的Description和Order屬性。ModelMetadata的Watermark屬性通過DisplayAttribute的GetPromp方法的返回值初始化。

1: public class ModelMetadata          
2: {          
3:     //其他成員          
4:     public virtual string DisplayName { get; set; }          
5:     public virtual string ShortDisplayName { get; set; }          
6:     public virtual string Description { get; set; }          
7:     public virtual int    Order { get; set; }          
8:     public virtual string Watermark {  get;  set; }          
9: }    

由於DisplayAttribute的GetName方法的返回值和DisplayNameAttribute的DisplayName屬性最終都用於設定ModelMetadata的DisplayName屬性,如果這兩個屬性同時應用到相同的目標元素上並且對顯示名稱作出了不同的設定,那麼DisplayAttribute特性具有更高的優先順序。 如下面的程式碼片斷所示,我們將DisplayAttribute和DisplayNameAttribute特性應用到了資料型別Model的相應的屬性上。其中屬性Bar上應用了DisplayNameAttribute並將顯示名稱設定為“Bar”,而屬性Baz上同時應用了DisplayAttribute和DisplayNameAttribute特性並分別講顯示名稱設定為“BAZ”和“Baz”。

1: public class Model          
2: {          
3:     public string Foo { get; set; }          
4:            
5:     [DisplayName("Bar")]          
6:     public string Bar { get; set; }          
7:            
8:     [Display(Name = "BAZ")]          
9:     [DisplayName("baz")]         
10:     public string Baz { get; set; }         
11: }    

現在我們通過如下程式碼通過呼叫輔助方法GetModelMetadata<TModel>或者定義在Model類上的三個屬性對應的ModelMetadata物件,並將其DisplayName屬性值打印出來。

1: ModelMetadata foo = GetModelMetadata<Model>("Foo");          
2: ModelMetadata bar = GetModelMetadata<Model>("Bar");          
3: ModelMetadata baz = GetModelMetadata<Model>("Baz");         
4:            
5: Console.WriteLine("Foo: {0}", foo.DisplayName ?? "N/A");          
6: Console.WriteLine("Bar: {0}", bar.DisplayName ?? "N/A");          
7: Console.WriteLine("Baz: {0}", baz.DisplayName ?? "N/A");    

上面的測試程式執行之後會在控制檯上產生如下的輸出結果,從這我們可以看到對於同時應用了DisplayAttribute和DisplayNameAttribute特性的Baz屬性,對應ModelMetadata的DisplayName屬性與DisplayAttribute是一致的。

1: Foo: N/A

2: Bar: Bar

3: Baz: BAZ

六、RequiredAttribute

我們來介紹最終一個標註特性RequiredAttribute。顧名思義,RequiredAttribute特性將目標元素設定為必需的資料成員。如下面的程式碼片斷所示,和DataTypeAttribute特性一樣,RequiredAttribute也是一個驗證特性。其AllowEmptyStrings屬性表示作為必需資料成員的目標元素是否接受一個空字串,預設情況下是不允許的。

1: [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false)]          
2: public class RequiredAttribute : ValidationAttribute          
3: {          
4:     public RequiredAttribute();          
5:     public bool AllowEmptyStrings { get; set; }          
6: }    

對於應用了RequiredAttribute特性的資料成員,對應ModelMetadata的IsRequired屬性將會被設定為True。如下面的程式碼片斷所示,該屬性是一個可讀寫的屬性。

1: public class ModelMetadata          
2: {          
3:     //其他成員          
4:     public virtual bool IsRequired { get; set; }              
5: }       

ASP.NET MVC Model元資料及其定製: 初識Model元資料

ASP.NET MVC Model元資料及其定製: Model元資料的定製 ASP.NET MVC Model元資料及其定製:一個重要的介面IMetadataAware