ASP.NET Web API Model-ModelMetadata


ASP.NET Web API Model-ModelMetadatac#



前言瀏覽器

前面的幾個篇幅主要圍繞控制器的執行過程,奈何執行過程當中包含的知識點太龐大了,只能一部分一部分的去講解,在上兩篇中咱們看到在控制器方法選擇器根據請求選定了控制器方法後會生成對應的描述對象以後進入過濾器執行過程當中,以後也是咱們所講的在受權過濾器執行以後會執行對Model的系列操做,中間包括Model元數據解析、Model綁定、Model驗證,最後會經過Web API框架的獨有的方式也就是ParameterBinding參數綁定來執行,在這些操做完畢以後會開始執行行爲過濾器,可在控制器方法執行先後進行攔截,從技術方向來講這是AOP思想的一種實現,從業務邏輯上來說也是一種依賴注入,扯遠了,本篇就來談談在ASP.NET Web API框架中的Model元數據,也就是ModelMetadata.框架




Model-ModelMetadata(對象介紹、基礎知識篇)ide

在講解Model綁定以前我仍是先來說解一下在Web API中的Model元數據,在我前面的ASP.NET MVC隨筆系列彙總中對MVC框架中的Model的元數據有過講解,對MVC有了解的朋友能夠去看看,在ASP.NET Web API框架中的Model元數據的設計思想和MVC框架中的是同樣的,都是對Model進行類型解析,而後已樹形結構呈現或者是給須要它的地方使用。爲何說是樹形結構呢?咱們來看一下示例,後面再結合對象類型這麼一講解你們就差很少該明白了。函數

 

咱們先無論在框架中是怎麼表示的,先來看一下定義的Modelui

示例代碼1-1this

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元數據事後包含的信息。spa

示例代碼1-2prototype

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

wKioL1QW37Tyt0-IAAIr2f90HhA068.jpg


根據圖1所示的這樣,而後咱們再看代碼1-2的實現,最後咱們看一下執行的結果。

2

wKiom1QW37WTid81AALB5hbdWqU648.jpg


2中所示就是從客戶端和瀏覽器共同訪問返回的結果值,都是同樣的。

 

下面咱們來說解一下相關的對象類型。

3

wKioL1QW39-zv8zFAAEPClHIkmE572.jpg


咱們先來看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-7CachedDataAnnotationsModelMetadata類型繼承的是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-8PrototypeCache屬性對應的是什麼類型,是一個泛型類型,而在CachedDataAnnotationsModelMetadata類型繼承的時候泛型類型咱們能夠在代碼1-7中看到,也就是代碼1-9定義的類型。

 

最後咱們看下示意圖。

4

wKiom1QW4CeAxB4VAAChgnhCyd8480.jpg

最後總結一句在Model元數據初始化的時候便會完成一些初始值的初始化,而對於Model上的行爲設置,則須要經過CachedDataAnnotationsMetadataAttributes類型來執行設置了,而CachedDataAnnotationsMetadataAttributes類型中對應的幾個屬性的類型你們一試便知。

相關文章
相關標籤/搜索