Silverlight驗(yàn)證機(jī)制主要內(nèi)容分析
Silverlight開(kāi)發(fā)工具主要應(yīng)用于多媒體方面,可以幫助開(kāi)發(fā)人員實(shí)現(xiàn)多平臺(tái)的音頻視頻處理,創(chuàng)建出一個(gè)適合開(kāi)發(fā)需求的WEB應(yīng)用程序。我們將會(huì)在這篇文章中針對(duì)Silverlight驗(yàn)證機(jī)制做一個(gè)詳細(xì)的介紹。#t#
我們?cè)陧?xiàng)目中使用了Silverlight驗(yàn)證機(jī)制。一開(kāi)始,感覺(jué)很不錯(cuò):可以用標(biāo)注的方式聲明驗(yàn)證邏輯,自動(dòng)設(shè)置校驗(yàn)控件,自動(dòng)驗(yàn)證數(shù)據(jù)——一切似乎很好。但是很快我發(fā)現(xiàn),Silverlight的校驗(yàn)機(jī)制也是存在嚴(yán)重限制的。
對(duì)于不熟悉Silverlight驗(yàn)證機(jī)制的朋友,我可以在這里作一個(gè)簡(jiǎn)單的介紹。關(guān)鍵在于System.ComponentModel.DataAnnotations這個(gè)程序集,它提供了一些標(biāo)記屬性,你可以為實(shí)體添加這些標(biāo)記,然后在編寫實(shí)體讀寫方法的時(shí)候添加一些觸發(fā)校驗(yàn)邏輯的樁代碼,那么內(nèi)置有數(shù)據(jù)校驗(yàn)功能的控件(比如Label和DataForm等)就能自動(dòng)識(shí)別、并按照你設(shè)定的值來(lái)進(jìn)行校驗(yàn)。
下面是從Pro Silverlight 3 for C#中摘抄的一段代碼。即使不看手冊(cè),其中Silverlight驗(yàn)證機(jī)制規(guī)定的驗(yàn)證邏輯是很容易看懂的。
- [StringLength(25)]
- [Display(Name = "Model Name",
Description = "This is the retail
product name.")]- public string ModelName
- {
- get { return modelName; }
- set
- {
- ValidationContext context = new
ValidationContext(this, null, null);- context.MemberName = "ModelNumber";
- Validator.ValidateProperty
(value, context);- modelName = value;
- OnPropertyChanged(new Property
ChangedEventArgs("ModelName"));- }
- }
Silverlight驗(yàn)證機(jī)制看起來(lái)很簡(jiǎn)單,而且我們使用的開(kāi)頭一段時(shí)間內(nèi)運(yùn)行得也相當(dāng)不錯(cuò),省去了很多手工校驗(yàn)的工作。直到有一天我們創(chuàng)建了某個(gè)新實(shí)體的時(shí)候,麻煩來(lái)了。
問(wèn)題是這樣的,項(xiàng)目需求要求我們保存某些客戶信息,其中Email是必須填寫的。實(shí)現(xiàn)此邏輯只要為屬性加上一個(gè)Required標(biāo)注即可。但問(wèn)題在于,盡管Email是必須填寫的,但我們卻無(wú)法為它提供一個(gè)合理的默認(rèn)值,所以開(kāi)始的時(shí)候此屬性是空字符串。另一方面,這個(gè)實(shí)體最初是從服務(wù)器端通過(guò)序列化得到的,而進(jìn)行序列化和反序列化的時(shí)候也會(huì)調(diào)用Setter,從而調(diào)用校驗(yàn)邏輯,拋出異常——這是我們不希望的行為。雖然不希望,我們卻不能去掉它,如果去掉的話,那么Silverlight的校驗(yàn)邏輯就不能工作了!
此問(wèn)題的關(guān)鍵點(diǎn)在于,序列化的時(shí)候需要調(diào)用實(shí)體的Setter,界面綁定的時(shí)候也要調(diào)用Setter,但兩種情況下需要的行為卻是不同的。創(chuàng)建一個(gè)新實(shí)體的時(shí)候,其中某些屬性有可能是無(wú)效的,但我們并不能因此阻止用戶創(chuàng)建新對(duì)象,這時(shí)候是應(yīng)當(dāng)禁用驗(yàn)證邏輯的。 那么接下來(lái)的問(wèn)題就是,實(shí)體的Setter中能不能識(shí)別到是在哪一種情況下調(diào)用的,從而打開(kāi)或關(guān)閉驗(yàn)證呢?
開(kāi)始我想到了Environment.StackTrace,根據(jù)調(diào)用堆棧來(lái)判斷運(yùn)行環(huán)境,應(yīng)該可以識(shí)別出代碼運(yùn)行的場(chǎng)合。但是實(shí)驗(yàn)一下就發(fā)現(xiàn):此路不通。StackTrace這個(gè)屬性在Silverlight版本的CLR中是根本沒(méi)有提供的,于是這個(gè)方向被堵死了。
有的組員提出,是否可以設(shè)置一個(gè)提示性的初始值,比如“<請(qǐng)?zhí)顚?gt;”?這個(gè)建議很快被否決了,因?yàn)橐笥脩魜?lái)刪掉無(wú)效的值再重新輸入并不合理,也不友好。
然后又有人說(shuō),是否可以根據(jù)實(shí)體的Id來(lái)判斷,如果是0則表示是新建的對(duì)象,不需要校驗(yàn)? 這也是不可行的,因?yàn)樾陆ǖ膶?duì)象在提交的時(shí)候同樣需要校驗(yàn)。
當(dāng)然還有一個(gè)辦法是為界面綁定和數(shù)據(jù)傳遞分別生成兩套實(shí)體,一套有數(shù)據(jù)校驗(yàn),一套沒(méi)有,然后寫代碼來(lái)在它們之間進(jìn)行轉(zhuǎn)換。但是想想也可以知道,這樣工作量實(shí)在太大了,也增加了維護(hù)的難度。
最終我們采取了一個(gè)比較笨的辦法:為實(shí)體添加一個(gè)IsUIBinding標(biāo)志,一開(kāi)始為false,在綁定到界面之前設(shè)置為true,提交服務(wù)器之前再?gòu)?fù)原為false。這樣是可以解決問(wèn)題了,不過(guò)程序員的負(fù)擔(dān)就更重了——必須記住在合適的時(shí)候修改這個(gè)標(biāo)記,否則程序就會(huì)出現(xiàn)bug。
這個(gè)結(jié)果讓我對(duì)Silverlight驗(yàn)證框架感到有點(diǎn)遺憾。Silverlight的驗(yàn)證方法過(guò)于嚴(yán)格——一旦數(shù)據(jù)不合法,ValidationException就會(huì)拋出,于是所有后續(xù)代碼都無(wú)法執(zhí)行,如果運(yùn)行環(huán)境沒(méi)有做好處理此異常的準(zhǔn)備的話,那么整個(gè)程序都會(huì)出錯(cuò)。而其他的場(chǎng)景——比如序列化的時(shí)候是沒(méi)有辦法處理此異常的,這大大限制了校驗(yàn)機(jī)制的應(yīng)用場(chǎng)景。
其實(shí)從設(shè)計(jì)上看,Silverlight驗(yàn)證機(jī)制使用了ValidationResult來(lái)收集校驗(yàn)失敗信息,那么理論上講,不使用異常,而根據(jù)ValidationResult收集的結(jié)果來(lái)判斷也是完全可能的。但最終Silverlight還是采用了異常的方法。不過(guò)盡管有此遺憾,Silverlight的校驗(yàn)機(jī)制對(duì)于一般的數(shù)據(jù)驗(yàn)證還是不錯(cuò)的,目前我們也不太可能拋開(kāi)它去完全實(shí)現(xiàn)一套自己的校驗(yàn)方法,只有在編程的時(shí)候多加注意了。