模型与元数据(Model & Model Metadata)
ASP.NET MVC中有一个模型的概念,实际上模型就是一个类型,Model表示的就是最终要绑定到View视图页面上的数据而已。
模型元数据
模型元数据用ModelMetadata类表示,它描述Model自身的类型、成员的类型、自身和成员的特性等信息,它的作用有两点:1.控制模型在视图上的呈现方式。2.为模型绑定验证逻辑。ModelMetadata元数据对象描述了与它关联在一起的Model的数据结构,以及Model的每个数据成员的一些特性。正是有了Model元数据的存在,才使模板化HTML的呈现机制成为可能。要创建一个模型的元数据,只需要在ModelMetadata构造函数中将Model的类型作为参数传递进去即可。ModelMetadata支撑了ASP.NET MVC的Model验证体系,因为针对Model的验证规则正是定义在Model元数据中。
ModelMetadata ( ModelMetadataProvider provider , Type containerType , Func<object> modelAccessor , Type modelType , string propertyName );
//构造函数,传递Model的类型作为参数创建出Model的元数据对象
Provider
//Model元数据的提供程序。
ModelType
//模型的类型。
IsComplexType
//是否是复杂类型。复杂类型即不是基元类型和值类型的引用类型
//如果Model支持从基元字符串转换为类类型,则IsComplexType将为fale,这可以通过TypeConverterAttribute特性描述当前Model是否可由字符串转为为Model类型
//如果是,则IsComplexType返回false
//示例:
[ TypeConverter ( typeof ( AnimalTypeConvert ) )]
public class Animal
{
public string Name { get; set; }
}
public class AnimalTypeConvert : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom ( ITypeDescriptorContext context , Type sourceType )
{
return sourceType == typeof ( string );
}
public override object ConvertFrom ( ITypeDescriptorContext context , CultureInfo culture , object value )
{
if ( value is string )
{
Animal a = new Animal { Name = value.ToString ( ) };
return a;
}
return base.ConvertFrom ( context , culture , value );
}
}
//测试:
string animal = "蚂蚁";
AnimalTypeConvert AnimalTypeConvert = new AnimalTypeConvert ( );
Animal a = ( Animal ) AnimalTypeConvert.ConvertFrom ( animal );
string name = a.Name; //print 蚂蚁
IsNullableValueType
//如果该类型可为 null,则为 true;否则为 false
ContainerType
//包容当前类型的类型,比如Animal包含Person类型,则Person的ContainerType为Animal
IsReadOnly
//该模型是否是只读的
IsRequired
//如果该模型是必需的,则为 true;否则为 false。
PropertyName
//模型的名称
Properties
//用于描述模型属性的模型元数据对象的集合。
AdditionalValues
//包含有关模型的其他元数据的字典,可通过在模型或模型成员上运用System.Web.Mvc.AdditionalMetadataAttribute特性来为它们增加自定义用于描述信息的属性
TemplateHint
//一个提示,建议要为此模型使用哪种模板。
FromLambdaExpression<TParameter, TValue> ( Expression<Func<TParameter , TValue>> expression , ViewDataDictionary<TParameter> viewData );
//从模型的 System.Linq.Expressions.Expression 参数返回元数据
GetValidators ( ControllerContext context );
//模型的验证程序的列表。
TemplateHint
//获取应用在元数据上的模板,比如Html模板、WPF模板、MVC模板,此属性可通过UIHintAttribute特性对元数据进行配置,TemplateHint的值=UIHintAttribute.UIHint
数据注解
特性运用在模型和模型的成员上,这样可以影响模型的元数据,而元数据也提供某些属性与特性进行关联。也即如果将某个类型放进ModelMetadata的构造函数就可以创建出该模型的元数据对象,这个对象提供一些属性或方法可以获取该模型的元数据信息。
UIHintAttribute特性
作用:ModelMetadata的TemplateHint属性表示运用在模型上的模板,这些模板可以是Html模板、WPF模板、MVC模板等等。此属性关联在模型上指定的UIHint特性。UIHint可以在一个模型、模型成员上运用多次,在MVC框架中,展示层的类型如果是Mvc则会优先使用该UIHint,如果没有指定展示层的类型则默认使用首次出现的UIHint
UIHint
//模板的名称,对应于元数据的TemplateHint属性
PresentationLayer
//展示层的类型
示例:以下创建了一个DemoModel模型,在它的成员上运用了UIHint特性,其中Bar和Baz成员运用了多次UIHint,Bar的UIHint特性使用了Mvc,所以该特性指定的模板会被确定为最终要使用的模板。
{
public string Foo { get; set; }
[UIHint ( "Template A" )]
[UIHint ( "Template B" , "Mvc" )]
public string Bar { get; set; }
[UIHint ( "Template A" )]
[UIHint ( "Template B" )]
[UIHint ( "Template C" )]
public string Baz { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index ( )
{
ModelMetadata modelMetadat = new ModelMetadata ( ModelMetadataProviders.Current , null , null , typeof ( DemoModel ) , null );
return View ( modelMetadat );
}
}
在视图中读取DemoModel的属性成员的元数据的TemplateHint信息
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<style>
body{padding:0;margin:0;font:normal 12px/24px "5FAE8F6F96C59ED1";color:#444;text-align: center;}
table{width:500px;border:0;margin:100px auto 0;text-align:center;border-collapse:collapse;border-spacing:0;}
table th{background: #09007c;font-weight:normal;line-height:30px;font-size:14px;color:#FFF;}
table tr:nth-child(odd){background:#F4F4F4;}
table td:nth-child(even){color:#C00;}
table tr:nth-child(5){background:#73B1E0;color:#FFF;}
table tr:hover{background:#73B1E0;color:#FFF;}
table td,table th{border:1px solid #EEE;}
</style>
</head>
<body>
<table>
@{
<tr>
<th>DemoModel的属性</th>
<th>元数据TemplateHint</th>
</tr>
foreach ( var p in Model.Properties )
{
<tr>
@{
<td>@p.PropertyName</td>
<td>@(p.TemplateHint??"N/A")</td>
}
</tr>
}
}
</table>
</body>
</html>
HiddenInputAttribute特性
作用:当使用Html.EditorFor()方法时,将运用了HiddenInput特性的模型或模型成员生成为隐藏域,HiddenInput对应于元数据的TemplateHint,而HiddenInput的DisplayValue则对应于元数据的HideSurroundingHtml。如果一个模型或模型成员上同时运用了UIHint和HiddenInput,默认UIHint的优先级更高。
HiddenInputAttribute ( bool DisplayValue )
DisplayValue
//一个bool值,指示该隐藏域的前面是否应以只读文本显示模型或模型成员的值,对应于元数据的HideSurroundingHtml属性
示例:使用Html.EditorFor方法将模型的成员以输入框的方式被创建,但设为隐藏域的成员将以隐藏域的方式被创建
{
public string name { get; set; }
[HiddenInput] //生成隐藏域且显示age的值
public string age { get; set; }
[HiddenInput ( DisplayValue = false )] //生成隐藏域且不显示gender的值
public string gender { get; set; }
}
public class DefaultController : Controller
{
public ActionResult Index()
{
return View ( new DemoModel { name = "sam" , age = "19" , gender = "男" } );
}
}
在视图页面中做测试
<form>
@Html.EditorFor ( m => m.name )
@Html.EditorFor ( m => m.age )
@Html.EditorFor ( m => m.gender )
</form>
ScaffoldColumnAttribute特性
作用:ScaffoldColumn指示当调用Html.EditorFor、Html.DisplayFor()方法创建编辑(input)、文本(label)时,运用了此特性的模型或模型成员将不会显示,也即EditorFor()、DisplayFor()方法将变得无效。经测试此特性没有效果,原因不明。ScaffoldColumn特性对应于元数据的ShowForDisplay、ShowForEdit属性。
RequiredAttribute特性
作用:使属性为必填项,与DataTypeAttribute一样,它们都派生自ValidationAttribute。
AllowEmptyStrings
//获取或设置一个值,该值指示是否允许空字符串,默认false。
ErrorMessageString
//获取或设置在验证失败时显示的错误消息
DataTypeAttribute特性
作用:用于标注数据应显示的数据格式,比如时间、货币等等。
DataType
//一个DataType枚举,指定模型或模型成员显示的数据格式类型
CustomDataType
//用户自定义的数据显示格式类型
DisplayFormat
//一个DisplayFormatAttribute特性,参看DisplayFormatAttribute特性
GetDataTypeName ( )
//如果需要自定义数据显示的格式类型,则通过从DataTypeAttribute派生,重写此虚方法。对应于元数据的DataTypeName属性
IsValid
//是否验证数据有效性
HtmlEncode
//默认情况下,模型或模型成员的值如果包含Html则会在视图上显示时应用Html格式,此属性可指示是否需要编码Html,默认false(应用Html格式),设为true则会将Html显示为可见的字符串
NullDisplayText
//指示模型或模型成员的值为null时应显示的字符,对应于元数据的NullDisplayText属性
ConvertEmptyStringToNull
//指示模型或模型成员的值为""时,是否转换为null,对应于元数据的ConvertEmptyStringToNull属性
ApplyFormatInEditMode
//指示模型或模型成员的值处于编辑模式时,是否格式化模型或模型成员的值
DataFormatString
//搭配ApplyFormatInEditMode使用,提供在编辑模式下格式化模型或模型成员的值
DisplayAttribute特性(System.ComponentModel.DataAnnotations)
作用:为在用户界面中使用的实体类型和成员指定可本地化的字符串,这些信息是展示给用户看见的数据。类似的有DisplayNameAttribute,但后者只提供简单的功能。
ResourceType
//本地化显示的资源文件类型,参考:多国语言的简单实现
Name
//为模型或模型成员设置在视图上显示的名称,也即定义别名。对应于元数据的DisplayName属性
ShortName
//为模型或模型成员设置在视图上显示的简短的名称,也即定义简短的别名。对应于元数据的ShortDisplayName属性
Description
//为模型或模型成员设置描述性文本,对应于元数据的Description属性
Order
//为模型或模型成员设置排序权重,默认10000。对应于元数据的Order属性
Prompt
//为模型或模型成员设置一个水印,此水印将会呈现在视图上
GetName()
//返回一个值,用于在 UI 中的字段的显示。对应于元数据的DisplayName属性
GetShortName()
//类似GetName方法,返回一个值,用于在UI中的字段的简洁显示。对应于元数据的ShortDisplayName属性
GetDescription()
//获取描述信息,对应于元数据的Description属性
GetOrder()
//获取对列排序所采取的顺序,默认值10000,对应于元数据的Order属性
GetPrompt()
//获取在UI中的水印的显示,对应于元数据的Watermark属性
例子:
{
public class Animal
{
[Display(Name ="姓名")] //如果没有应用Display特性,则试图页面将显示'AnimalName'而非'姓名'
public string AnimalName { get; set; }
}
public class DefaultController : Controller
{
public ActionResult Test()
{
return View();
}
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test</title>
<style>
body{background:black;color:white;font-size:12px;font-family:Arial;}
</style>
</head>
<body>
<div>
@Html.EditorForModel()
</div>
</body>
</html>
EditableAttribute特性(对应元数据的IsReadOnly)
作用:指示模型或模型成员是否允许被编辑
AllowEdit
//是否允许编辑,对应于元数据的IsReadOnly属性
ReadOnlyAttribute特性
IsReadOnly
//是否只读,对应于元数据的IsReadOnly属性
DisplayName特性
DisplayName
//设置属性、事件、无参函数的显示名称。对应于元数据的DisplayName
在模型上应用自定义特性
IMetadataAware接口
如果将某个类型(模型)放进ModelMetadata的构造函数就可以创建出该模型的元数据对象,这个对象就代表了该模型的元数据。它提供一些属性或方法来返回该模型的元数据信息。在模型上应用的数据注解(特性)也会被提取出来用于初始化元数据对象。而假如你想为模型增加一些自定义的元数据或修改已经应用在模型上的某些元数据,那么你可以考虑使用AdditionalMetadataAttribute或AllowHtmlAttribute特性,这两个特性都实现了IMetadataAware接口,IMetadataAware接口提供OnMetadataCreate方法,该方法要求接收一个ModelMetadata对象(模型的元数据)作为参数,这样,就可以在OnMetadataCreate方法中(创建ModelMetadata时)增加一些元数据或修改已经应用在模型上的某些元数据。
AdditionalMetadataAttribute特性
作用:此特性类实现了IMetadataAware接口以支持其他自定义的元数据。
name
//自定义元数据的名称
value
//自定义元数据的值
AllowHtmlAttribute特性
作用:为了防止用户通过脚本注入攻击应用程序(XSS)所提供的一道验证开关。
//ASP.NET MVC默认会验证提交的数据是否包含Html,如果有则会把异常抛到客户端,如果应用了此特性则不会验证