Shuhari

Silverlight校验框架的限制——基于异常的验证机制

我们在项目中使用了Silverlight 3中新增的校验机制。一开始,感觉很不错:可以用标注的方式声明验证逻辑,自动设置校验控件,自动验证数据——一切似乎很好。但是很快我发现,Silverlight的校验机制也是存在严重限制的。

 

对于不熟悉Silverlight校验机制的朋友,我可以在这里作一个简单的介绍。关键在于System.ComponentModel.DataAnnotations这个程序集,它提供了一些标记属性,你可以为实体添加这些标记,然后在编写实体读写方法的时候添加一些触发校验逻辑的桩代码,那么内置有数据校验功能的控件(比如Label和DataForm等)就能自动识别、并按照你设定的值来进行校验。

 

下面是从Pro Silverlight 3 for C#中摘抄的一段代码。即使不看手册,其中规定的验证逻辑是很容易看懂的:

[StringLength(25)]
[Display(Name 
= "Model Name", Description = "This is the retail product name.")]
public string ModelName
{
    get { return modelName; }
    set
    {
        ValidationContext context = new ValidationContext(thisnullnull);
        context.MemberName 
= "ModelNumber";
        Validator.ValidateProperty(value, context);
        modelName 
= value;
        OnPropertyChanged(
new PropertyChangedEventArgs("ModelName"));
    }
}

 

看起来很简单,而且我们使用的开头一段时间内运行得也相当不错,省去了很多手工校验的工作。直到有一天我们创建了某个新实体的时候,麻烦来了。

 

问题是这样的,项目需求要求我们保存某些客户信息,其中Email是必须填写的。实现此逻辑只要为属性加上一个Required标注即可。但问题在于,尽管Email是必须填写的,但我们却无法为它提供一个合理的默认值,所以开始的时候此属性是空字符串。另一方面,这个实体最初是从服务器端通过序列化得到的,而进行序列化和反序列化的时候也会调用Setter,从而调用校验逻辑,抛出异常——这是我们不希望的行为。虽然不希望,我们却不能去掉它,如果去掉的话,那么Silverlight的校验逻辑就不能工作了!

 

此问题的关键点在于,序列化的时候需要调用实体的Setter,界面绑定的时候也要调用Setter,但两种情况下需要的行为却是不同的。创建一个新实体的时候,其中某些属性有可能是无效的,但我们并不能因此阻止用户创建新对象,这时候是应当禁用验证逻辑的。 那么接下来的问题就是,实体的Setter中能不能识别到是在哪一种情况下调用的,从而打开或关闭验证呢?

 

开始我想到了Environment.StackTrace,根据调用堆栈来判断运行环境,应该可以识别出代码运行的场合。但是实验一下就发现:此路不通。StackTrace这个属性在Silverlight版本的CLR中是根本没有提供的,于是这个方向被堵死了。

 

有的组员提出,是否可以设置一个提示性的初始值,比如“<请填写>”?这个建议很快被否决了,因为要求用户来删掉无效的值再重新输入并不合理,也不友好。

然后又有人说,是否可以根据实体的Id来判断,如果是0则表示是新建的对象,不需要校验? 这也是不可行的,因为新建的对象在提交的时候同样需要校验。

当然还有一个办法是为界面绑定和数据传递分别生成两套实体,一套有数据校验,一套没有,然后写代码来在它们之间进行转换。但是想想也可以知道,这样工作量实在太大了,也增加了维护的难度。

 

最终我们采取了一个比较笨的办法:为实体添加一个IsUIBinding标志,一开始为false,在绑定到界面之前设置为true,提交服务器之前再复原为false。这样是可以解决问题了,不过程序员的负担就更重了——必须记住在合适的时候修改这个标记,否则程序就会出现bug。

 

这个结果让我对Silverlight验证框架感到有点遗憾。Silverlight的验证方法过于严格——一旦数据不合法,ValidationException就会抛出,于是所有后续代码都无法执行,如果运行环境没有做好处理此异常的准备的话,那么整个程序都会出错。而其他的场景——比如序列化的时候是没有办法处理此异常的,这大大限制了校验机制的应用场景。其实从设计上看,Silverlight使用了ValidationResult来收集校验失败信息,那么理论上讲,不使用异常,而根据ValidationResult收集的结果来判断也是完全可能的。但最终Silverlight还是采用了异常的方法。不过尽管有此遗憾,Silverlight的校验机制对于一般的数据验证还是不错的,目前我们也不太可能抛开它去完全实现一套自己的校验方法,只有在编程的时候多加注意了。

posted on 2009-11-13 16:48  Shuhari  阅读(727)  评论(6编辑  收藏  举报

导航