您好,登錄后才能下訂單哦!
ASP.NET Web API Model-ModelMetadata
前言
前面的幾個篇幅主要圍繞控制器的執行過程,奈何執行過程中包含的知識點太龐大了,只能一部分一部分的去講解,在上兩篇中我們看到在控制器方法選擇器根據請求選定了控制器方法后會生成對應的描述對象之后進入過濾器執行過程中,之后也是我們所講的在授權過濾器執行之后會執行對Model的系列操作,中間包括Model元數據解析、Model綁定、Model驗證,最后會通過Web API框架的獨有的方式也就是ParameterBinding參數綁定來執行,在這些操作完畢之后會開始執行行為過濾器,可在控制器方法執行前后進行攔截,從技術方向來說這是AOP思想的一種實現,從業務邏輯上來講也是一種依賴注入,扯遠了,本篇就來談談在ASP.NET Web API框架中的Model元數據,也就是ModelMetadata.
Model-ModelMetadata(對象介紹、基礎知識篇)
在講解Model綁定之前我還是先來講解一下在Web API中的Model元數據,在我前面的ASP.NET MVC隨筆系列匯總中對MVC框架中的Model的元數據有過講解,對MVC有了解的朋友可以去看看,在ASP.NET Web API框架中的Model元數據的設計思想和MVC框架中的是一樣的,都是對Model進行類型解析,然后已樹形結構呈現或者是給需要它的地方使用。為什么說是樹形結構呢?我們來看一下示例,后面再結合對象類型這么一講解大家就差不多該明白了。
我們先不管在框架中是怎么表示的,先來看一下定義的Model:
示例代碼1-1
publicclassEmployeesInfo { [Display(Description="員工姓名")] publicstringName { get; set; } [Display(Description="員工年齡")] publicintAge { get; set; } [Display(Description="員工性別")] publicstringSex { get; set; } [Display(Description="員工地址信息")] publicAddressAddressInfo { get; set; } } publicclassAddress { [Display(Description="地址信息")] publicstringAddressInfo { get; set; } [Display(Description="郵編")] publicstringZipCode { get; set; } }
在代碼1-1中我定義了一個Model類型為EmployeesInfo,表示員工的信息,在這其中有幾個屬性,其中AddressInfo屬性是下面的Address類型,這里是為了說明Model元數據所表示的對象存在兩種類型,一種是簡單類型,另一種是復雜類型。至于這個類型怎么判斷下面會說,我們還是先來看示例代碼,看下Model經過框架生成為Model元數據過后包含的信息。
示例代碼1-2
publicclassMetadataController : ApiController { publicstringGet() { EmployeesInfoemployeesInfo=newEmployeesInfo() { Name="JinYuan", Age=24, Sex="男", AddressInfo=newAddress() { AddressInfo="南京市", ZipCode="210000" } }; ModelMetadatamodelMetadata=this.Configuration.Services.GetModelMetadataProvider().GetMetadataForType(()=>employeesInfo, employeesInfo.GetType()); StringBuilderstrBuilder=newStringBuilder(); ModelMetadataAnalysis(strBuilder, modelMetadata); returnstrBuilder.ToString(); } privatevoidModelMetadataAnalysis(StringBuilderstringBuilder, ModelMetadatamodelMetadata) { if (modelMetadata.IsComplexType==true) { foreach (varmetadatainmodelMetadata.Properties) { ModelMetadataAnalysis(stringBuilder, metadata); } } else { stringBuilder.AppendLine(modelMetadata.Description).AppendLine("Value:"+modelMetadata.Model); } } }
在代碼1-2中,我首先定義了一個控制器,并且定義了一個Get()方法,返回類型為string類型,然后在Get()方法中實例化Model,也就是我們代碼1-1中的定義,在這之后我根據當前控制器基類所包含的HttpConfiguration類型的屬性Configuration,從ServicesContainer 類型的Services屬性中獲取到Model元數據提供程序,從而使用這個提供程序來根據Model類型生成Model元數據,這里起初生成好的Model元數據也就是modelMetadata變量,它暫時只是一個類型的的表示,也就是EmployeesInfo類型,并且其中并不包含EmployeesInfo類型中屬性的元數據,這里要注意的是不包含只是暫時的。
然后下面使用了一個方法ModelMetadataAnalysis(),首先判斷當前的Model元數據表示的類型是不是復雜類型,如果是的話就讀取這個Model元數據中的Properties屬性,注意了在讀取的時候,也就是ModelMetadataAnalysis()方法中的參數modelMetadata就會開始使用自身的Model元數據提供程序來生成自身所表示類型下屬性的Model元數據。由此可以看到上面的代碼實現中只是對簡單類型進行了輸出。我們看一下示意圖。
圖1
根據圖1所示的這樣,然后我們再看代碼1-2的實現,最后我們看一下執行的結果。
圖2
圖2中所示就是從客戶端和瀏覽器共同訪問返回的結果值,都是一樣的。
下面我們來講解一下相關的對象類型。
圖3
我們先來看ModelMetadata類型,從圖3中我們可以看到ModelMetadata類型在命名空間System.Web.Http.Metadata下,我們就先來看一下ModelMetadata的定義,
示例代碼1-3
publicclassModelMetadata { publicModelMetadata(ModelMetadataProviderprovider, TypecontainerType, Func<object>modelAccessor, TypemodelType, stringpropertyName); publicvirtualDictionary<string, object>AdditionalValues { get; } publicTypeContainerType { get; } publicvirtualboolConvertEmptyStringToNull { get; set; } publicvirtualstringDescription { get; set; } publicvirtualboolIsComplexType { get; } publicboolIsNullableValueType { get; } publicvirtualboolIsReadOnly { get; set; } publicobjectModel { get; set; } publicTypeModelType { get; } publicvirtualIEnumerable<ModelMetadata>Properties { get; } publicstringPropertyName { get; } protectedModelMetadataProviderProvider { get; set; } publicstringGetDisplayName(); publicvirtualIEnumerable<System.Web.Http.Validation.ModelValidator>GetValidators(IEnumerable<System.Web.Http.Validation.ModelValidatorProvider>validatorProviders); }
代碼1-3中定義了ModelMetadata類型,我們就從構造函數開始講解。
在構造函數中有五個參數,這些參數有的是跟屬性對應的,就拿第一個參數ModelMetadataProvider類型來說,它對應的就是ModelMetadata類型中的Provider屬性,有的朋友會問這個屬性干什么的?還記得上面的示例中講過,在Model元數據是復雜類型的時候從Properties屬性中獲取當前所表示類型下的Model元數據,就是在獲取的時候拿什么生成?就是用的這個Provider屬性對應的元數據提供程序,這里也講一下Properties屬性,從它的定義中可以看到,是個IEnumerable<ModelMetadata>類型的屬性,也就是為什么上面我所說的以樹形結構的方式展現給我們看的原因。下面說到構造函數中的第二個參數實例類型,就是這個元數據對象所表示的、所對應的類型的容器類型,第三個參數比較有意思,是個Func<Object>委托,它是用來獲取當前元數據對象所表示類型的實例值,這也就是ModelMetadata類型中Model屬性的屬性值的由來,第四個參數就是當前元數據對應的對象類型,也就是對應著ModelMetadata類型中ModelType屬性值,最后一個參數表示屬性名稱,也對應著ModelMetadata類型中的PropertyName屬性值。
下面說說ModelMetadata類型中的其他屬性值,AdditionalValues表示容器屬性,可自行添加任何額外值在其中以鍵值隊的方式表示,這個很多框架中設計對象時都會有,可以是object類型,不過這里使用鍵值隊來表示。
IsComplexType屬性表示當前Model元數據對象所表示的類型是否是復雜類型,這個怎么判斷的呢?
publicvirtualboolIsComplexType { get { return!TypeHelper.HasStringConverter(this.ModelType); } }
就是看類型是否可以轉換為String類型。
IsReadOnly屬性下面再講,因為在初始化一個Model元數據的時候得到的信息只有這么多,而IsReadOnly屬性則是通過ModelAttribute來控制的。
我們再來看一下ModelMetadataProvider
示例代碼1-4
publicabstractclassModelMetadataProvider { protectedModelMetadataProvider(); publicabstractIEnumerable<ModelMetadata>GetMetadataForProperties(objectcontainer, TypecontainerType); publicabstractModelMetadataGetMetadataForProperty(Func<object>modelAccessor, TypecontainerType, stringpropertyName); publicabstractModelMetadataGetMetadataForType(Func<object>modelAccessor, TypemodelType); }
在代碼1-4中我們看到Model元數據提供程序ModelMetadataProvider類型中有三個抽象方法,本身也是抽象類,這三個方法的含義來給大家解釋一下。
GetMetadataForProperties()方法是根據容器實例、容器的類型來獲取容器中所有屬性的元數據類型。
GetMetadataForProperty()方法則是根據一個獲取容器實例的委托、容器類型,和要返回的屬性元數據的屬性名稱。
GetMetadataForType()方法就是根據一個類型來獲取這個類型所所表示的元數據,不過委托參數是要能獲取到這個類型的實例。
下面我們回到代碼1-2中,在我們獲取Model元數據提供程序的地方,上面也說過了我們是從哪里獲取到的,現在我們就來看看具體的類型,
示例代碼1-5
this.SetSingle<ModelMetadataProvider>(newDataAnnotationsModelMetadataProvider());
那我們就來看看DataAnnotationsModelMetadataProvider類型中的定義。
示例代碼1-6
publicclassDataAnnotationsModelMetadataProvider : AssociatedMetadataProvider<CachedDataAnnotationsModelMetadata> { publicDataAnnotationsModelMetadataProvider(); protectedoverrideCachedDataAnnotationsModelMetadataCreateMetadataFromPrototype(CachedDataAnnotationsModelMetadataprototype, Func<object>modelAccessor); protectedoverrideCachedDataAnnotationsModelMetadataCreateMetadataPrototype(IEnumerable<Attribute>attributes, TypecontainerType, TypemodelType, stringpropertyName); }
我們先不管DataAnnotationsModelMetadataProvider類型,而是看它中定義的函數的返回類型CachedDataAnnotationsModelMetadata類型。
CachedDataAnnotationsModelMetadata類型
示例代碼1-7
publicclassCachedDataAnnotationsModelMetadata : CachedModelMetadata<CachedDataAnnotationsMetadataAttributes> { publicCachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadataprototype, Func<object>modelAccessor); publicCachedDataAnnotationsModelMetadata(DataAnnotationsModelMetadataProviderprovider, TypecontainerType, TypemodelType, stringpropertyName, IEnumerable<Attribute>attributes); [SecuritySafeCritical] protectedoverrideboolComputeConvertEmptyStringToNull(); [SecuritySafeCritical] protectedoverridestringComputeDescription(); [SecuritySafeCritical] protectedoverrideboolComputeIsReadOnly(); }
從代碼1-7中我們可以看到CachedDataAnnotationsModelMetadata類型繼承自CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>類型,也就是CachedModelMetadata<TPrototypeCache>類型。
讓我們先來看一下CachedModelMetadata<TPrototypeCache>類型
代碼1-8
publicabstractclassCachedModelMetadata<TPrototypeCache> : ModelMetadata { protectedCachedModelMetadata(CachedModelMetadata<TPrototypeCache>prototype, Func<object>modelAccessor); protectedCachedModelMetadata(DataAnnotationsModelMetadataProviderprovider, TypecontainerType, TypemodelType, stringpropertyName, TPrototypeCache prototypeCache); publicoverridesealedboolConvertEmptyStringToNull { get; set; } publicoverridesealedstringDescription { get; set; } publicoverridesealedboolIsComplexType { get; } publicoverridesealedboolIsReadOnly { get; set; } protected TPrototypeCache PrototypeCache { get; set; } protectedvirtualboolComputeConvertEmptyStringToNull(); protectedvirtualstringComputeDescription(); protectedvirtualboolComputeIsComplexType(); protectedvirtualboolComputeIsReadOnly(); }
看到這里怎么感覺這么亂的呢亂七八糟的,比較煩躁啊!!!!大家莫慌。
前面說到了Model元數據在初始化的時候便會初始了很多的值,可是大家有沒有想過Model元數據的真正的作用?在初始化的時候可以說我們的Model元數據已經包含了所表示對象的對象類型和值一些基本的信息,但是我們要給Model上附加額外的動作,比如說控制這個某某屬性是只讀的或者是讓某某屬性換一個顯示的方式,這種事情是在初始化的時候Model元數據做不了的,那咋整?
看代碼1-8中最下面的Compute開頭的四個方法,這四個方法就是用來獲取我們設置的動作然后設置到Model元數據上的,當然看了一下實現并不是我們想要的結果,而是通過代碼1-7中定義的CachedDataAnnotationsModelMetadata類型來實現的,在代碼1-7中CachedDataAnnotationsModelMetadata類型繼承的是CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>類型,現在我們就來看看CachedDataAnnotationsMetadataAttributes類型的定義。
CachedDataAnnotationsMetadataAttributes類型
示例代碼1-9
publicclassCachedDataAnnotationsMetadataAttributes { publicCachedDataAnnotationsMetadataAttributes(IEnumerable<Attribute>attributes); publicSystem.ComponentModel.DataAnnotations.DisplayAttributeDisplay { get; protectedset; } publicSystem.ComponentModel.DataAnnotations.DisplayFormatAttributeDisplayFormat { get; protectedset; } publicSystem.ComponentModel.DataAnnotations.EditableAttributeEditable { get; protectedset; } publicReadOnlyAttributeReadOnly { get; protectedset; } }
代碼1-9中的CachedDataAnnotationsMetadataAttributes類型就是應用于元數據特性多個類型的封裝,這里我們暫且不管,知道CachedDataAnnotationsMetadataAttributes類型是個啥就行了。現在我們回到代碼1-7中看看那幾個方法的具體實現方式。
代碼1-10
[SecuritySafeCritical] protectedoverridestringComputeDescription() { if (base.PrototypeCache.Display==null) { returnbase.ComputeDescription(); } returnbase.PrototypeCache.Display.GetDescription(); }
我挑了其中一個,這里可以清楚的看到是要調用基類當中的PrototypeCache屬性,那么這里的PrototypeCache屬性對應的是什么類型?
大家可以看一下代碼1-8中PrototypeCache屬性對應的是什么類型,是一個泛型類型,而在CachedDataAnnotationsModelMetadata類型繼承的時候泛型類型我們可以在代碼1-7中看到,也就是代碼1-9定義的類型。
最后我們看下示意圖。
圖4
最后總結一句在Model元數據初始化的時候便會完成一些初始值的初始化,而對于Model上的行為設置,則需要通過CachedDataAnnotationsMetadataAttributes類型來執行設置了,而CachedDataAnnotationsMetadataAttributes類型中對應的幾個屬性的類型大家一試便知。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。