现在的位置: 首页 > 综合 > 正文

ASP.NET MVC Model元数据及其定制: Model元数据的定制

2012年04月24日 ⁄ 综合 ⁄ 共 6365字 ⁄ 字号 评论关闭

在《上篇》我们已经提到过了,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,前者以只读的形式显示出来;后者却在界面上看不到。

image

如下所示的用于呈现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

抱歉!评论已关闭.