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 = ,
Sex = "男",
AddressInfo = new Address()
{
AddressInfo = "南京市",
ZipCode = ""
}
};
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/

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

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

  1. Model Validation in ASP.NET Web API

    Model Validation in ASP.NET Web API 原文:http://www.asp.net/web-api/overview/formats-and-model-binding ...

  2. Asp.Net Web API 2第十五课——Model Validation(模型验证)

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文参考链接文章地址htt ...

  3. ASP.NET Web API Model-ModelBinder

    ASP.NET Web API Model-ModelBinder 前言 本篇中会为大家介绍在ASP.NET Web API中ModelBinder的绑定原理以及涉及到的一些对象模型,还有简单的Mod ...

  4. 新作《ASP.NET Web API 2框架揭秘》正式出版

    我觉得大部分人都是“眼球动物“,他们关注的往往都是目光所及的东西.对于很多软件从业者来说,他们对看得见(具有UI界面)的应用抱有极大的热忱,但是对背后支撑整个应用的服务却显得较为冷漠.如果我们将整个“ ...

  5. ASP.NET Web API 2框架揭秘

    ASP.NET Web API 2框架揭秘(.NET领域再现力作顶级专家精讲微软全新轻量级通信平台) 蒋金楠 著   ISBN 978-7-121-23536-8 2014年7月出版 定价:108.0 ...

  6. 《ASP.NET Web API 2框架揭秘》

    <ASP.NET Web API 2框架揭秘> 基本信息 作者: 蒋金楠 出版社:电子工业出版社 ISBN:9787121235368 上架时间:2014-7-5 出版日期:2014 年7 ...

  7. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  8. ASP.NET Web API Model-ActionBinding

    ASP.NET Web API Model-ActionBinding 前言 前面的几个篇幅把Model部分的知识点划分成一个个的模块来讲解,而在控制器执行过程中分为好多个过程,对于控制器执行过程(一 ...

  9. ASP.NET Web API Model-ParameterBinding

    ASP.NET Web API Model-ParameterBinding 前言 通过上个篇幅的学习了解Model绑定的基础知识,然而在ASP.NET Web API中Model绑定功能模块并不是被 ...

随机推荐

  1. 【原创分享·支付宝支付】HBuilder打包APP调用支付宝客户端支付

    前言 最近有点空余时间,所以,就研究了一下APP支付.前面很早就搞完APP的微信支付了,但是由于时间上和应用上的情况,支付宝一直没空去研究.然后等我空了的时候,发现支付宝居然升级了支付逻辑,虽然目前还 ...

  2. MySQL主从环境下存储过程,函数,触发器,事件的复制情况

    下面,主要是验证在MySQL主从复制环境下,存储过程,函数,触发器,事件的复制情况,这些确实会让人混淆. 首先,创建一张测试表 mysql),age int); Query OK, rows affe ...

  3. 数据图表插件Echarts(一)

    一.引言 最近做一个智慧城市项目,项目中需要图表和报表进行数据分析,从网上找了很多,最后找到了百度开放的echarts,一个很强大的插件. 二.介绍 ECharts,缩写来自Enterprise Ch ...

  4. 趣说游戏AI开发:曼哈顿街角的A*算法

    0x00 前言 请叫我标题党!请叫我标题党!请叫我标题党!因为下面的文字既不发生在美国曼哈顿,也不是一个讲述美国梦的故事.相反,这可能只是一篇没有那么枯燥的关于算法的文章.A星算法,这个在游戏寻路开发 ...

  5. C#创建、安装、卸载、调试Windows Service(Windows 服务)的简单教程

    前言:Microsoft Windows 服务能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这 ...

  6. PHP static静态属性和静态方法

    这里分析了php面向对象中static静态属性和静态方法的调用.关于它们的调用(能不能调用,怎么样调用),需要弄明白了他们在内存中存放位置,这样就非常容易理解了.静态属性.方法(包括静态与非静态)在内 ...

  7. NodeJs支付宝移动支付签名及验签

    非常感谢 :http://www.jianshu.com/p/8513e995ff3a?utm_campaign=hugo&utm_medium=reader_share&utm_co ...

  8. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  9. 如何区别数据库删除语句drop与delete与truncate?

    1.delete:删除数据表中的行(可以删除某一行,也可以在不删除数据表的情况下删除所有行) 删除某一行:delete from 数据表名称 where 列名称=值: 删除所有行:delete*fro ...

  10. SQL-类型转换函数

    CAST ( expression AS data_type)CONVERT ( data_type, expression,[style]) Select '您的班级编号'+ 1  错误这里+是数学 ...