问题:
属性的参数只能是有限的几种类型
It is really unbelievable but real. This code will not work:
难以置信,却是真的。
这段代码可以正常运行:
[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
public class Range : Attribute
{
public decimal Max { get; set; }
public decimal Min { get; set; }
}
这段代码却有编译错误:“Min”不是一个合法的属性命名参数,因为它不是一个合法的属性参数类型
public class Item
{
[Range(Min=0m,Max=1000m)] //compile error:'Min' is not a valid named attribute argument because it is not a valid attribute parameter type
public decimal Total { get; set; }
}
回答:
This is a CLR restriction. Only primitive constants or arrays of primitives can be used as attribute parameters. The reason why is that an attribute must be encoded entirely in metadata. This is different than a method body which is coded in IL. Using MetaData only severely restricts the scope of values that can be used. In the current version of the CLR, metadata values are limited to primitives, null, types and arrays of primitives (may have missed a minor one).
这是一个CLR(Common Language Runtime,公共运行语言运行时)限制。只有原始类型的常量或者原始类型的数组可以作为属性参数。这是由于一个属性必须整个编进元数据。这个和函数体生成中间语言(IL,Intermediate Language)是不相同的。元数据的使用严格地限制了取值范围。在CLR的当前版本,元数据的值仅限于基本类型,空,原始类型及其数组(不排除会有一小部分不支持)。
Taken from this answer by JaredPar.
Decimals while a basic type are not a primitive type and hence cannot be represented in metadata which prevents it from being an attribute parameter.
小数是一个基本类型,而基本类型又不是原始类型,所以不能用在元数据,元数据阻止它成为一个属性参数。
问题:
Basic types 和primitive type的差别:
Primitive type翻译成原始类型,是编程语言建立起来的基础,它相对于复合类型。但复合类型的划分又与编程语言有关系,例如,在C语言中,字符串是一个复合类型,不过在后来的Basic中,字串是原始类型。原始类型的变量存放在堆区,按值传递。常见的如字符,整数,浮点等。
Basic types翻译成基本类型,它在原始类型的基础上再进行构造,我理解成结构体(在C/C++中)或者类。一般存放在栈中(如类的对象),按引用传递。常见的为一般的类。
问题:
CLR是什么?
CLR常用简写词语,CLR是公共语言运行时,Common Language Runtime)和Java虚拟机一样也是一个运行时环境,它负责资源管理(内存分配和垃圾收集),并保证应用和底层操作系统之间必要的分离。
IL是什么?
IL是.NET框架中中间语言(Intermediate Language)的缩写。使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(Intermedate Language)的代码。
使用中间语言的优点有两点,一是可以实现平台无关性,既与特定CPU无关;二是只要把.NET框架某种语言编译成IL代码,就实现.NET框架中语言之间的交互操作。(《C#程序设计及应用教程》(第2版),马骏 主编)
问题:
属性的默认验证有哪些?
.NET MVC Models定义字段过程中的验证属性参数:
[Required()]
指定数据字段值是必需的。
包含参数:
ErrorMessage 验证失败的错误消息;
ErrorMessageResourceType 错误消息资源类型;
ErrorMessageResourceName 错误消息资源名称;
[DataType]
指定与某个数据字段关联的附加类型的名称(一个 DataType 枚举值,如 EmailAddress、Url 或 Password)。
[DisplayName]
数据字段显示名称
[StringLength]
指定数据字段中允许的字符串最大长度。
[RegularExpression]
指定数据字段值必须与指定的正则表达式匹配。
[Range]
指定数据字段值的数值范围限制。
[PropertiesMustMatch][ValidatePasswordLength]
问题:
在属性的默认验证不能符合要求的情况下,如何添加自定义的验证属性?
下面我们来实现一个DateAttribute属性来实现日期格式和日期范围的检查。新建一个动态链接库Biz.Web,添加System.ComponentModel.DataAnnotations引用,新建一个类如下:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;
namespace Biz.Web
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class DateAttribute:ValidationAttribute
{
public DateAttribute()
{
//set default max and min time according to sqlserver datetime type
MinDate = new DateTime(1753, 1, 1).ToString();
MaxDate = new DateTime(9999, 12, 31).ToString();
}
public string MinDate
{
get;
set;
}
public string MaxDate
{
get;
set;
}
private DateTime minDate, maxDate;
//indicate if the format of the input is really a datetime
private bool isFormatError=true;
public override bool IsValid(object value)
{
//ignore it if no value
if (value == null || string.IsNullOrEmpty(value.ToString()))
return true;
//begin check
string s = value.ToString();
minDate = DateTime.Parse(MinDate);
maxDate = DateTime.Parse(MaxDate);
bool result = true;
try
{
DateTime date = DateTime.Parse(s);
isFormatError = false;
if (date > maxDate || date < minDate)
result = false;
}
catch (Exception)
{
result = false;
}
return result;
}
public override string FormatErrorMessage(string name)
{
if (isFormatError)
return "请输入合法的日期";
return base.FormatErrorMessage(name);
}
}
}
主要实现IsValid方法,如有需要也可以实现FormatErrorMessage方法。要注意属性的参数只能是有限的几种类型,参考这里。写好以后,把HelloWorld项目添加Biz.Web引用,然后就可以使用了,例如:
[Display(Name="发布日期"),Date(MaxDate="2012-01-01",ErrorMessage="2012地球灭亡啦")]
public DateTime ReleaseDate { get; set; }
看下效果:
当然,这种方式有许多限制,主要是属性参数的限制,只能是基本类型,而且只能是常量。更复杂的验证规则的添加请看这里:
Implement Remote Validation in ASP.NET MVC.
注:原来对日期的默认验证:
[Display(Name="发布日期")]
public DateTime ReleaseDate { get; set; }
显然,添加自定义验证之后,可以写上Date()进行更复杂的自定义验证。