·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> ASP.NET Web API Model-ModelMetadata

ASP.NET Web API Model-ModelMetadata

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

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>    {