目錄 一、ModelValidatorProviders 二、ModelValidator、ModelValidatorProvider和ModelValidatorProviders 三、CompositeModelValidator 四、執行個體示範:探測CompositeModelValidator采用的驗證行為
值得一提的是,ModelValidatorProviderCollection定義了一個GetValidators方法用于傳回一個通過集合中每個ModelValidatorProvider建立的ModelValidator集合。在這個方法中,指定的Model中繼資料和Controller上下文會被傳入每個ModelValidatorProvider對象的GetValidators方法,得到的每個ModelValidator對象将會作為最終傳回的ModelValidator集合的元素。
在預設的情況下,通過ModelValidatorProviders的Providers表示注冊的ModelValidatorProvider清單會包含三個對象,對應着我們前面介紹的三種ModelValidatorProvider類型,即DataAnnotationsModelValidatorProvider、ClientDataTypeModelValidatorProvider和DataErrorInfoPropertyModelValidator。
如果我們需要添加一個自定義ModelValidatorProvider,可以直接将相應的對象添加到ModelValidatorProviders的Providers清單中。如果需要采用自定義ModelValidatorProvider來替換掉現有的ModelValidatorProvider,比如我們建立了一個擴充的DataAnnotationsModelValidatorProvider,還需要将現有的ModelValidatorProvider從該清單中移除。
實作在ModelValidatorProvider中的ModelValidator提供機制是基于Model中繼資料和Controller上下文的,實際上用于描述Model中繼資料的ModelMetadata類型同樣定義了一個GetValidators方法用于根據指定的Controller上下文的所有ModelValidator對象。如下面的代碼片斷所示,該方法直接調用了通過ModelValidatorProviders的Providers屬性表述的ModelValidatorProviderCollection對象的同名方法。
上面我們介紹用于進行Model驗證的ModelValidator,用于提供ModelValidator的ModelValidatorProvider,以及用于注冊ModelValidatorProvider的ModelValidatorProviders,整個ModelValidator的提供機制以此三類元件為核心,下圖所示的UML展現了它們之間的關系。

雖然CompositeModelValidator僅僅是定義在程式集System.Web.Mvc.dll中的一個私有類型,但是它在ASP.NET
MVC的Model驗證系統中具有重要的地位,可以說真正用于Model驗證的ModelValidator就是這麼一個對象。從如下是以的成員定義代碼并不能看出CompositeModelValidator有何特别之處。
從其類型名稱可以看出CompositeModelValidator實際上并不是一個真正對Model對象實施驗證的ModelValidator,它是一系列ModelValidator的組合,它根據基于Model本身類型及其屬性的Model中繼資料動态地擷取相應的ModelValidator(通過調用ModelMetadata的GetValidators方法)對Model對象實施驗證。
定義在Validate方法中的驗證邏輯是這樣的:CompositeModelValidator通過在構造函數中初始化的表示驗證對象類型的Model中繼資料的ModelMetadata對象的Properties屬性得到基于屬性的Model中繼資料清單。然後周遊該清單的每個ModelMetadata對象,調用其GetValidators方法得到一組用于驗證屬性值得ModelValidator清單,然後使用該ModelValidator清單依次對相應的屬性值進行驗證,驗證失敗得到的ModelValidationResult對象被添加到最終傳回的ModelValidationResult集合中。
隻有在所有屬性值都通過驗證的情況下,CompositeModelValidator采用調用基于被驗證類型Model中繼資料的ModelMetadata對象的GetValidators方法得到在類型級别ModelValidator清單對指定的資料對象實施驗證,驗證失敗得到的ModelValidationResult對象被添加到最終傳回的ModelValidationResult集合中。
抽象類ModelValidator具有一個靜态的GetModelValidator方法根據指定的Model中繼資料和Controller上下文得到相應的ModelValidator對象。如下面的代碼片斷所示,該方法傳回的正是一個CompositeModelValidator對象。
為了使讀者對CompositeModelValidator的驗證邏輯具有一個深刻的了解,我們來示範一個具體的Model驗證的執行個體。我們建立了如果一個名稱為AlwaysFailsAttribute的驗證特性。如下面的代碼片斷所示,重寫的IsValid方法總是傳回False,意味着針對資料的驗證總是會失敗。我們還重寫了隻讀屬性TypeId,讓它能夠真正能夠唯一辨別一個AlwaysFailsAttribute特性執行個體,具體原因我們會在本章後續部分講述。
我們将AlwaysFailsAttribute應用到具有如下定義的表示聯系人的Contact類型上。如下面的代碼片斷所示,我們在Contact和Address的類型和屬性都應用了該特性,并且指定了相應的錯誤消息。
在一個通過Visual Studio的ASP.NET
MVC項目模闆建立的空Web應用中,我們建立了具有如下定義的預設HomeController類。在Action方法Index中,我們使用目前的ModelMetadataProvider建立了基于Contact類型的ModelMetadata,然後調用ModelValidator的靜态方法GetValidator方法得到基于該ModelMetadata和ControllerContext的ModelValidator對象(一個CompositeModelValidator對象)。最後我們通過該ModelValidator對象來驗證手工建立的Contact對象,并将得到的ModelValidationResult對象的MemberName和Message屬性呈現出來。
運作該程式後會在浏覽器中得到如下所示的輸出結果。這樣的輸出結果至少反映了兩個問題,其一,CompositeModelValidator對資料的驗證并不是遞歸進行的,因為隻有應用在Contact屬性上的驗證特性參與了驗證,而應用在Address類型屬性上的驗證特性則沒有被使用;其二,在屬性認證失敗的情況下是不會進行基于類型的驗證的,因為浏覽器中并不存在應用在Contact類型上的驗證特性對應的輸出。
上面的輸出結果還反映了另外一個細節,針對某個屬性的ModelValidator清單會同時包含應用在屬性和屬性對應類型的驗證特性生成的ModelValidator。輸出的最後兩個ModelValidationResult都是針對Contact的Address屬性的,分别對應着應用在Contact的Address屬性和Address類型上的兩個AlwaysFailsAttribute特性。現在我們按照如下的方式将應用在Contact的四個屬性以及Address類型上的AlwaysFailsAttribute特性注冊掉,隻保留應用在Contact類型的AlwaysFailsAttribute特性。
在此運作我們的程式将會在浏覽器中得到如下的輸出結果。不難看出輸出的ModelValidationResult對應于着應用在Contact類型上的AlwaysFailsAttribute特性,這充分反映了上面所說的:基于類型的驗證隻有在基于屬性的驗證失敗的情況下才會進行。
<a href="http://www.cnblogs.com/artech/archive/2012/06/01/model-validator-01.html">ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidator</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/02/model-validator-02.html">ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidatorProvider</a>
<a href="http://www.cnblogs.com/artech/archive/2012/06/03/model-validator-03.html">ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidatorProviders</a>
作者:蔣金楠
微信公衆賬号:大内老A
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識别二維碼)關注個人公衆号(原來公衆帳号蔣金楠的自媒體将會停用)。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
<a href="http://www.cnblogs.com/artech/archive/2012/06/03/model-validator-03.html" target="_blank">原文連結</a>