ASP.NET Web API Model-ModelMetadata
成都創(chuàng)新互聯(lián)公司主營通州網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app開發(fā)定制,通州h5成都微信小程序搭建,通州網(wǎng)站營銷推廣歡迎通州等地區(qū)企業(yè)咨詢前言
前面的幾個(gè)篇幅主要圍繞控制器的執(zhí)行過程,奈何執(zhí)行過程中包含的知識點(diǎn)太龐大了,只能一部分一部分的去講解,在上兩篇中我們看到在控制器方法選擇器根據(jù)請求選定了控制器方法后會生成對應(yīng)的描述對象之后進(jìn)入過濾器執(zhí)行過程中,之后也是我們所講的在授權(quán)過濾器執(zhí)行之后會執(zhí)行對Model的系列操作,中間包括Model元數(shù)據(jù)解析、Model綁定、Model驗(yàn)證,最后會通過Web API框架的獨(dú)有的方式也就是ParameterBinding參數(shù)綁定來執(zhí)行,在這些操作完畢之后會開始執(zhí)行行為過濾器,可在控制器方法執(zhí)行前后進(jìn)行攔截,從技術(shù)方向來說這是AOP思想的一種實(shí)現(xiàn),從業(yè)務(wù)邏輯上來講也是一種依賴注入,扯遠(yuǎn)了,本篇就來談?wù)勗贏SP.NET Web API框架中的Model元數(shù)據(jù),也就是ModelMetadata.
Model-ModelMetadata(對象介紹、基礎(chǔ)知識篇)
在講解Model綁定之前我還是先來講解一下在Web API中的Model元數(shù)據(jù),在我前面的ASP.NET MVC隨筆系列匯總中對MVC框架中的Model的元數(shù)據(jù)有過講解,對MVC有了解的朋友可以去看看,在ASP.NET Web API框架中的Model元數(shù)據(jù)的設(shè)計(jì)思想和MVC框架中的是一樣的,都是對Model進(jìn)行類型解析,然后已樹形結(jié)構(gòu)呈現(xiàn)或者是給需要它的地方使用。為什么說是樹形結(jié)構(gòu)呢?我們來看一下示例,后面再結(jié)合對象類型這么一講解大家就差不多該明白了。
我們先不管在框架中是怎么表示的,先來看一下定義的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中我定義了一個(gè)Model類型為EmployeesInfo,表示員工的信息,在這其中有幾個(gè)屬性,其中AddressInfo屬性是下面的Address類型,這里是為了說明Model元數(shù)據(jù)所表示的對象存在兩種類型,一種是簡單類型,另一種是復(fù)雜類型。至于這個(gè)類型怎么判斷下面會說,我們還是先來看示例代碼,看下Model經(jīng)過框架生成為Model元數(shù)據(jù)過后包含的信息。
示例代碼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中,我首先定義了一個(gè)控制器,并且定義了一個(gè)Get()方法,返回類型為string類型,然后在Get()方法中實(shí)例化Model,也就是我們代碼1-1中的定義,在這之后我根據(jù)當(dāng)前控制器基類所包含的HttpConfiguration類型的屬性Configuration,從ServicesContainer 類型的Services屬性中獲取到Model元數(shù)據(jù)提供程序,從而使用這個(gè)提供程序來根據(jù)Model類型生成Model元數(shù)據(jù),這里起初生成好的Model元數(shù)據(jù)也就是modelMetadata變量,它暫時(shí)只是一個(gè)類型的的表示,也就是EmployeesInfo類型,并且其中并不包含EmployeesInfo類型中屬性的元數(shù)據(jù),這里要注意的是不包含只是暫時(shí)的。
然后下面使用了一個(gè)方法ModelMetadataAnalysis(),首先判斷當(dāng)前的Model元數(shù)據(jù)表示的類型是不是復(fù)雜類型,如果是的話就讀取這個(gè)Model元數(shù)據(jù)中的Properties屬性,注意了在讀取的時(shí)候,也就是ModelMetadataAnalysis()方法中的參數(shù)modelMetadata就會開始使用自身的Model元數(shù)據(jù)提供程序來生成自身所表示類型下屬性的Model元數(shù)據(jù)。由此可以看到上面的代碼實(shí)現(xiàn)中只是對簡單類型進(jìn)行了輸出。我們看一下示意圖。
圖1
根據(jù)圖1所示的這樣,然后我們再看代碼1-2的實(shí)現(xiàn),最后我們看一下執(zhí)行的結(jié)果。
圖2
圖2中所示就是從客戶端和瀏覽器共同訪問返回的結(jié)果值,都是一樣的。
下面我們來講解一下相關(guān)的對象類型。
圖3
我們先來看ModelMetadata類型,從圖3中我們可以看到ModelMetadata類型在命名空間System.Web.Http.Metadata下,我們就先來看一下ModelMetadata的定義,
示例代碼1-3
publicclassModelMetadata { publicModelMetadata(ModelMetadataProviderprovider, TypecontainerType, Func
代碼1-3中定義了ModelMetadata類型,我們就從構(gòu)造函數(shù)開始講解。
在構(gòu)造函數(shù)中有五個(gè)參數(shù),這些參數(shù)有的是跟屬性對應(yīng)的,就拿第一個(gè)參數(shù)ModelMetadataProvider類型來說,它對應(yīng)的就是ModelMetadata類型中的Provider屬性,有的朋友會問這個(gè)屬性干什么的?還記得上面的示例中講過,在Model元數(shù)據(jù)是復(fù)雜類型的時(shí)候從Properties屬性中獲取當(dāng)前所表示類型下的Model元數(shù)據(jù),就是在獲取的時(shí)候拿什么生成?就是用的這個(gè)Provider屬性對應(yīng)的元數(shù)據(jù)提供程序,這里也講一下Properties屬性,從它的定義中可以看到,是個(gè)IEnumerable
下面說說ModelMetadata類型中的其他屬性值,AdditionalValues表示容器屬性,可自行添加任何額外值在其中以鍵值隊(duì)的方式表示,這個(gè)很多框架中設(shè)計(jì)對象時(shí)都會有,可以是object類型,不過這里使用鍵值隊(duì)來表示。
IsComplexType屬性表示當(dāng)前Model元數(shù)據(jù)對象所表示的類型是否是復(fù)雜類型,這個(gè)怎么判斷的呢?
publicvirtualboolIsComplexType { get { return!TypeHelper.HasStringConverter(this.ModelType); } }
就是看類型是否可以轉(zhuǎn)換為String類型。
IsReadOnly屬性下面再講,因?yàn)樵诔跏蓟粋€(gè)Model元數(shù)據(jù)的時(shí)候得到的信息只有這么多,而IsReadOnly屬性則是通過ModelAttribute來控制的。
我們再來看一下ModelMetadataProvider
示例代碼1-4
publicabstractclassModelMetadataProvider { protectedModelMetadataProvider(); publicabstractIEnumerableGetMetadataForProperties(objectcontainer, TypecontainerType); publicabstractModelMetadataGetMetadataForProperty(Func
在代碼1-4中我們看到Model元數(shù)據(jù)提供程序ModelMetadataProvider類型中有三個(gè)抽象方法,本身也是抽象類,這三個(gè)方法的含義來給大家解釋一下。
GetMetadataForProperties()方法是根據(jù)容器實(shí)例、容器的類型來獲取容器中所有屬性的元數(shù)據(jù)類型。
GetMetadataForProperty()方法則是根據(jù)一個(gè)獲取容器實(shí)例的委托、容器類型,和要返回的屬性元數(shù)據(jù)的屬性名稱。
GetMetadataForType()方法就是根據(jù)一個(gè)類型來獲取這個(gè)類型所所表示的元數(shù)據(jù),不過委托參數(shù)是要能獲取到這個(gè)類型的實(shí)例。
下面我們回到代碼1-2中,在我們獲取Model元數(shù)據(jù)提供程序的地方,上面也說過了我們是從哪里獲取到的,現(xiàn)在我們就來看看具體的類型,
示例代碼1-5
this.SetSingle(newDataAnnotationsModelMetadataProvider());
那我們就來看看DataAnnotationsModelMetadataProvider類型中的定義。
示例代碼1-6
publicclassDataAnnotationsModelMetadataProvider : AssociatedMetadataProvider{ publicDataAnnotationsModelMetadataProvider(); protectedoverrideCachedDataAnnotationsModelMetadataCreateMetadataFromPrototype(CachedDataAnnotationsModelMetadataprototype, Func modelAccessor); protectedoverrideCachedDataAnnotationsModelMetadataCreateMetadataPrototype(IEnumerable attributes, TypecontainerType, TypemodelType, stringpropertyName); }
我們先不管DataAnnotationsModelMetadataProvider類型,而是看它中定義的函數(shù)的返回類型CachedDataAnnotationsModelMetadata類型。
CachedDataAnnotationsModelMetadata類型
示例代碼1-7
publicclassCachedDataAnnotationsModelMetadata : CachedModelMetadata{ publicCachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadataprototype, Func modelAccessor); publicCachedDataAnnotationsModelMetadata(DataAnnotationsModelMetadataProviderprovider, TypecontainerType, TypemodelType, stringpropertyName, IEnumerable attributes); [SecuritySafeCritical] protectedoverrideboolComputeConvertEmptyStringToNull(); [SecuritySafeCritical] protectedoverridestringComputeDescription(); [SecuritySafeCritical] protectedoverrideboolComputeIsReadOnly(); }
從代碼1-7中我們可以看到CachedDataAnnotationsModelMetadata類型繼承自CachedModelMetadata
讓我們先來看一下CachedModelMetadata
代碼1-8
publicabstractclassCachedModelMetadata: ModelMetadata { protectedCachedModelMetadata(CachedModelMetadata prototype, Func 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元數(shù)據(jù)在初始化的時(shí)候便會初始了很多的值,可是大家有沒有想過Model元數(shù)據(jù)的真正的作用?在初始化的時(shí)候可以說我們的Model元數(shù)據(jù)已經(jīng)包含了所表示對象的對象類型和值一些基本的信息,但是我們要給Model上附加額外的動(dòng)作,比如說控制這個(gè)某某屬性是只讀的或者是讓某某屬性換一個(gè)顯示的方式,這種事情是在初始化的時(shí)候Model元數(shù)據(jù)做不了的,那咋整?
看代碼1-8中最下面的Compute開頭的四個(gè)方法,這四個(gè)方法就是用來獲取我們設(shè)置的動(dòng)作然后設(shè)置到Model元數(shù)據(jù)上的,當(dāng)然看了一下實(shí)現(xiàn)并不是我們想要的結(jié)果,而是通過代碼1-7中定義的CachedDataAnnotationsModelMetadata類型來實(shí)現(xiàn)的,在代碼1-7中CachedDataAnnotationsModelMetadata類型繼承的是CachedModelMetadata
CachedDataAnnotationsMetadataAttributes類型
示例代碼1-9
publicclassCachedDataAnnotationsMetadataAttributes { publicCachedDataAnnotationsMetadataAttributes(IEnumerableattributes); publicSystem.ComponentModel.DataAnnotations.DisplayAttributeDisplay { get; protectedset; } publicSystem.ComponentModel.DataAnnotations.DisplayFormatAttributeDisplayFormat { get; protectedset; } publicSystem.ComponentModel.DataAnnotations.EditableAttributeEditable { get; protectedset; } publicReadOnlyAttributeReadOnly { get; protectedset; } }
代碼1-9中的CachedDataAnnotationsMetadataAttributes類型就是應(yīng)用于元數(shù)據(jù)特性多個(gè)類型的封裝,這里我們暫且不管,知道CachedDataAnnotationsMetadataAttributes類型是個(gè)啥就行了。現(xiàn)在我們回到代碼1-7中看看那幾個(gè)方法的具體實(shí)現(xiàn)方式。
代碼1-10
[SecuritySafeCritical] protectedoverridestringComputeDescription() { if (base.PrototypeCache.Display==null) { returnbase.ComputeDescription(); } returnbase.PrototypeCache.Display.GetDescription(); }
我挑了其中一個(gè),這里可以清楚的看到是要調(diào)用基類當(dāng)中的PrototypeCache屬性,那么這里的PrototypeCache屬性對應(yīng)的是什么類型?
大家可以看一下代碼1-8中PrototypeCache屬性對應(yīng)的是什么類型,是一個(gè)泛型類型,而在CachedDataAnnotationsModelMetadata類型繼承的時(shí)候泛型類型我們可以在代碼1-7中看到,也就是代碼1-9定義的類型。
最后我們看下示意圖。
圖4
最后總結(jié)一句在Model元數(shù)據(jù)初始化的時(shí)候便會完成一些初始值的初始化,而對于Model上的行為設(shè)置,則需要通過CachedDataAnnotationsMetadataAttributes類型來執(zhí)行設(shè)置了,而CachedDataAnnotationsMetadataAttributes類型中對應(yīng)的幾個(gè)屬性的類型大家一試便知。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。