ASP.NET Web API Model-ModelMetadata

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

    public class EmployeesInfo
    {
        [Display(Description="员工姓名")]
        public string Name { get; set; }

        [Display(Description = "员工年龄")]
        public int Age { get; set; }

        [Display(Description = "员工性别")]
        public string Sex { get; set; }

        [Display(Description = "员工地址信息")]
        public Address AddressInfo { get; set; }
    }

    public class Address
    {
        [Display(Description = "地址信息")]
        public string AddressInfo { get; set; }

        [Display(Description = "邮编")]
        public string ZipCode { get; set; }
    }

在代码1-1中我定义了一个Model类型为EmployeesInfo,表示员工的信息,在这其中有几个属性,其中AddressInfo属性是下面的Address类型,这里是为了说明Model元数据所表示的对象存在两种类型,一种是简单类型,另一种是复杂类型。至于这个类型怎么判断下面会说,我们还是先来看示例代码,看下Model经过框架生成为Model元数据过后包含的信息。

示例代码1-2

    public class MetadataController : ApiController
    {
        public string Get()
        {
            EmployeesInfo employeesInfo = new EmployeesInfo()
            {
                Name = "JinYuan",
                Age = 24,
                Sex = "男",
                AddressInfo = new Address()
                {
                    AddressInfo = "南京市",
                    ZipCode = "210000"
                }
            };
            ModelMetadata modelMetadata = this.Configuration.Services.GetModelMetadataProvider().GetMetadataForType(()=>employeesInfo, employeesInfo.GetType());
            StringBuilder strBuilder = new StringBuilder();
            ModelMetadataAnalysis(strBuilder, modelMetadata);
            return strBuilder.ToString();

        }

        private void ModelMetadataAnalysis(StringBuilder stringBuilder, ModelMetadata modelMetadata)
        {
            if (modelMetadata.IsComplexType == true)
            {
                foreach (var metadata in modelMetadata.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

    public class ModelMetadata
    {
        public ModelMetadata(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName);
        public virtual Dictionary<string, object> AdditionalValues { get; }
        public Type ContainerType { get; }
        public virtual bool ConvertEmptyStringToNull { get; set; }
        public virtual string Description { get; set; }
        public virtual bool IsComplexType { get; }
        public bool IsNullableValueType { get; }
        public virtual bool IsReadOnly { get; set; }
        public object Model { get; set; }
        public Type ModelType { get; }
        public virtual IEnumerable<ModelMetadata> Properties { get; }
        public string PropertyName { get; }
        protected ModelMetadataProvider Provider { get; set; }
        public string GetDisplayName();
        public virtual IEnumerable<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元数据对象所表示的类型是否是复杂类型,这个怎么判断的呢?

    public virtual bool IsComplexType
    {
        get
        {
            return !TypeHelper.HasStringConverter(this.ModelType);
        }
    }

就是看类型是否可以转换为String类型。

IsReadOnly属性下面再讲,因为在初始化一个Model元数据的时候得到的信息只有这么多,而IsReadOnly属性则是通过ModelAttribute来控制的。

我们再来看一下ModelMetadataProvider

示例代码1-4

    public abstract class ModelMetadataProvider
    {
        protected ModelMetadataProvider();
        public abstract IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType);
        public abstract ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName);
        public abstract ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType);
    }

在代码1-4中我们看到Model元数据提供程序ModelMetadataProvider类型中有三个抽象方法,本身也是抽象类,这三个方法的含义来给大家解释一下。

GetMetadataForProperties()方法是根据容器实例、容器的类型来获取容器中所有属性的元数据类型。

GetMetadataForProperty()方法则是根据一个获取容器实例的委托、容器类型,和要返回的属性元数据的属性名称。

GetMetadataForType()方法就是根据一个类型来获取这个类型所所表示的元数据,不过委托参数是要能获取到这个类型的实例。

下面我们回到代码1-2中,在我们获取Model元数据提供程序的地方,上面也说过了我们是从哪里获取到的,现在我们就来看看具体的类型,

示例代码1-5

this.SetSingle<ModelMetadataProvider>(new DataAnnotationsModelMetadataProvider());

那我们就来看看DataAnnotationsModelMetadataProvider类型中的定义。

示例代码1-6

    public class DataAnnotationsModelMetadataProvider : AssociatedMetadataProvider<CachedDataAnnotationsModelMetadata>
    {
        public DataAnnotationsModelMetadataProvider();

        protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor);
        protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName);
    }

我们先不管DataAnnotationsModelMetadataProvider类型,而是看它中定义的函数的返回类型CachedDataAnnotationsModelMetadata类型。

CachedDataAnnotationsModelMetadata类型

示例代码1-7

    public class CachedDataAnnotationsModelMetadata : CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>
    {
        public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor);
        public CachedDataAnnotationsModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Type modelType, string propertyName, IEnumerable<Attribute> attributes);

        [SecuritySafeCritical]
        protected override bool ComputeConvertEmptyStringToNull();
        [SecuritySafeCritical]
        protected override string ComputeDescription();
        [SecuritySafeCritical]
        protected override bool ComputeIsReadOnly();
    }

从代码1-7中我们可以看到CachedDataAnnotationsModelMetadata类型继承自CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>类型,也就是CachedModelMetadata<TPrototypeCache>类型。

让我们先来看一下CachedModelMetadata<TPrototypeCache>类型

代码1-8

public abstract class CachedModelMetadata<TPrototypeCache> : ModelMetadata
    {
        protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor);
        protected CachedModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Type modelType, string propertyName, TPrototypeCache prototypeCache);
        public override sealed bool ConvertEmptyStringToNull { get; set; }
        public override sealed string Description { get; set; }
        public override sealed bool IsComplexType { get; }
        public override sealed bool IsReadOnly { get; set; }
        protected TPrototypeCache PrototypeCache { get; set; }

        protected virtual bool ComputeConvertEmptyStringToNull();
        protected virtual string ComputeDescription();
        protected virtual bool ComputeIsComplexType();
        protected virtual bool ComputeIsReadOnly();
    }

看到这里怎么感觉这么乱的呢乱七八糟的,比较烦躁啊!!!!大家莫慌。

前面说到了Model元数据在初始化的时候便会初始了很多的值,可是大家有没有想过Model元数据的真正的作用?在初始化的时候可以说我们的Model元数据已经包含了所表示对象的对象类型和值一些基本的信息,但是我们要给Model上附加额外的动作,比如说控制这个某某属性是只读的或者是让某某属性换一个显示的方式,这种事情是在初始化的时候Model元数据做不了的,那咋整?

看代码1-8中最下面的Compute开头的四个方法,这四个方法就是用来获取我们设置的动作然后设置到Model元数据上的,当然看了一下实现并不是我们想要的结果,而是通过代码1-7中定义的CachedDataAnnotationsModelMetadata类型来实现的,在代码1-7中CachedDataAnnotationsModelMetadata类型继承的是CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>类型,现在我们就来看看CachedDataAnnotationsMetadataAttributes类型的定义。

CachedDataAnnotationsMetadataAttributes类型

示例代码1-9

    public class CachedDataAnnotationsMetadataAttributes
    {
        public CachedDataAnnotationsMetadataAttributes(IEnumerable<Attribute> attributes);

        public System.ComponentModel.DataAnnotations.DisplayAttribute Display { get; protected set; }
        public System.ComponentModel.DataAnnotations.DisplayFormatAttribute DisplayFormat { get; protected set; }
        public System.ComponentModel.DataAnnotations.EditableAttribute Editable { get; protected set; }
        public ReadOnlyAttribute ReadOnly { get; protected set; }
    }

代码1-9中的CachedDataAnnotationsMetadataAttributes类型就是应用于元数据特性多个类型的封装,这里我们暂且不管,知道CachedDataAnnotationsMetadataAttributes类型是个啥就行了。现在我们回到代码1-7中看看那几个方法的具体实现方式。

代码1-10

    [SecuritySafeCritical]
    protected override string ComputeDescription()
    {
        if (base.PrototypeCache.Display == null)
        {
            return base.ComputeDescription();
        }
        return base.PrototypeCache.Display.GetDescription();
    }

我挑了其中一个,这里可以清楚的看到是要调用基类当中的PrototypeCache属性,那么这里的PrototypeCache属性对应的是什么类型?

大家可以看一下代码1-8中PrototypeCache属性对应的是什么类型,是一个泛型类型,而在CachedDataAnnotationsModelMetadata类型继承的时候泛型类型我们可以在代码1-7中看到,也就是代码1-9定义的类型。

最后我们看下示意图。

图4

最后总结一句在Model元数据初始化的时候便会完成一些初始值的初始化,而对于Model上的行为设置,则需要通过CachedDataAnnotationsMetadataAttributes类型来执行设置了,而CachedDataAnnotationsMetadataAttributes类型中对应的几个属性的类型大家一试便知。

作者:金源

出处:http://www.cnblogs.com/jin-yuan/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

时间: 2024-09-30 19:06:39

ASP.NET Web API Model-ModelMetadata的相关文章

ASP.NET Web API Model-ModelBinder

ASP.NET Web API Model-ModelBinder 前言 本篇中会为大家介绍在ASP.NET Web API中ModelBinder的绑定原理以及涉及到的一些对象模型,还有简单的Model绑定示例,在前面的篇幅中讲解了Model元数据.ValueProvider的模块,然后还有本篇的Model绑定的模块这些会结合到后面篇幅中的ParameterBinder模块中来使用,也就是说在ASP.NET Web API框架中绑定的方式有两种实现,都是通过ParameterBinder来对参

新作《ASP.NET Web API 2框架揭秘》开始全面接受预订

我觉得大部分人都是"眼球动物",他们关注的往往都是目光所及的东西.对于很多软件从业者来说,他们对看得见(具有UI界面)的应用抱有极大的热忱,但是对背后支撑整个应用的服务却显得较为冷漠.如果我们将整个"生态系统"比喻成海面上漂浮的冰山,我们所能看的到的只是露出水面的冰山一角,水面之下才是一个"庞然大物". 提到服务,我们自然想到Web Service.但是传统意义上的Web Service却有点名不副实,因为支撑它的其实不是Web而是SOAP,承载

ASP.NET Web API 2框架揭秘

ASP.NET Web API 2框架揭秘(.NET领域再现力作顶级专家精讲微软全新轻量级通信平台) 蒋金楠 著   ISBN 978-7-121-23536-8 2014年7月出版 定价:108.00元 732页 16开 编辑推荐 √ 这是一本注重实证的书,功能各异.多达120个可供下载的示例,大量最佳实践与实用性扩展,可直接用于解决实际开发问题. √ 全新的学习方法,通过完整论证来实现彻底的融会贯通. √ 本书可以作为讲设计架构的书来读,因为其以经过长期检验的经典架构作为学习素材,可很好地启

ASP.NET Web API基于OData的增删改查,以及处理实体间关系

本篇体验实现ASP.NET Web API基于OData的增删改查,以及处理实体间的关系. 首先是比较典型的一对多关系,Supplier和Product. public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public string Category { get; set; } [ForeignKey("Sup

ASP.Net Web API 的参数绑定[翻译]

原文地址:Parameter Binding in ASP.NET Web API 译文如下: 当Web API相应Controller的一个方法时,它必定存在一个设置参数的过程,叫作数据绑定.这篇文章描述了Web API如何绑定参数以及如何自定义绑定过程. 一般情况下,Web API绑定参数符合如下规则: 如果参数为简单类型,Web API 尝试从URI中获取.简单参数类型包含.Net源生类型(int,bool,double...),加上TimeSpan,DateTime,Guid,decim

Asp.Net Web API 2第三课——.NET客户端调用Web API

Asp.Net Web API 导航 Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web API第二课——CRUD操作http://www.cnblogs.com/aehyok/p/3434578.html 前言 本教程演示从一个控制台应用程序,使用HttpClient调用Web API.我们也将使用上一个教程中建立的Web API.你可以直接在http://www.cnblogs.com/

开始 ASP.NET Web API 2 之旅

HTTP不仅仅是提供网页而已.它同时也是一个用于公开服务和数据的强大的API平台.HTTP简单.灵活,而且无处不在.你能想到的几乎所有的平台,都会有一个HTTP库,因此HTTP服务可以达到广泛的客户端,包括浏览器.移动设备,和传统的桌面应用程序. ASP.NET Web API是一个用于构建基于.NET Framework 的 Web API 的框架.在本教程中,你将会使用ASP.NET Web API来创建返回一个产品列表的web API. 教程中使用的软件有: Visual Studio 2

ASP.NET Web API 过滤器创建、执行过程(二)

前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器中,当然默认的基础服务也是有的,并且根据这些提供程序所获得的的过滤器信息集合进行排序.本篇就会对过滤器在创建完之后所做的一系列操作进行讲解. ASP.NET Web API 过滤器创建.执行过程(二) FilterGrouping过滤器分组类型 FilterGrouping类型是ApiController类型中的私有类型,它的作用就如同它的命名一样,用来对过

ASP.NET Web API 基本操作(CRUD)

上一篇介绍了ASP.NET Web API的基本知识和原理,这一篇我们通过一个更直观的实例,对产品进行CRUD操作(Create/Read/Update/Delete)来继续了解一下它的基本应用. 创建ASP.NET Web API应用程序  在VS中选择创建一个ASP.NET Web Application应用程序,在向导的下一个窗口中选择Web API模板. 创建Model 这里我们在Models文件夹下创建一个简单的Product model类,用来传递数据. 在Models文件夹上点击右