ASP.NET MVC —— Model之一模型模板
http://www.cnblogs.com/lzhp/archive/2013/03/25/2981650.html
Mvc model系列文章主要分为三部分:Model Templates,Model Binding,Model Validation。本篇文章主要内容包括下面三个部分:
A.使用模板视图助手
B.自定义视图模板系统
C.理解元数据提供体系
一、使用模板视图助手
1.1助手体验
模板视图助手,我理解为MVC提供的根据model中定义的数据类型,来生成视图(View)标签的助手。显而易见其好处,当我们更改model中的数据类型时,不用担心要更改view。下面我们就来体验一下吧。
先建立一个空的mvc解决方案,起名为ModelTemplate,如下图
然后在models中新建一个类,在此只是为了演示,所以没有把下面三个类分开

    //定义人员类
    public  class Person
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }
    //定义人员住址类
    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }
    //定义人员角色类
    public enum Role
    {
        Admin,
        User,
        Guest
    }

然后在controllers中添加个HomeControl,在Index中添加下面的代码:

            Person myPerson = new Person
            {
                PersonId = 1,
                FirstName = "李",
                LastName = "占朋",
                BirthDate = DateTime.Parse("1988-1-15"),
                HomeAddress = new Address
                {
                    Line1 = "仓山区",
                    Line2 = "南后街",
                    City = "福州",
                    Country = "中国",
                    PostalCode = "350000"
                },
                IsApproved = true,
                Role = Role.User
            };
            return View(myPerson);

接着生成一下解决方案,在Index上面右击,添加一个视图:
在视图中添加下面的代码,并运行

<h4>人员</h4> <div class="field"> <label>姓名:</label> @Html.EditorFor(x => x.FirstName) @Html.EditorFor(x => x.LastName) </div> <div class="field"> <label>是否通知:</label> @Html.EditorFor(x => x.IsApproved) </div>

运行结果为:
会发现mvc framework为我们根据我们定义的数据类型生成了input和checkbox。当我们将IsApproved改为字符串,运行结果为:
不用更改前端,同样模板视图助手会根据类型去生成input。这样模板视图助手是不是很好用呢?除了可以生成input还可以生成其他的,如果我们想让前端显示的是只读的话,可以使用:@Html.DisplayFor(x=>x.IsApproved)。运行结果为:
除了可以使用强类型的,还可以使用下面的用法,后面直接跟出字段的字符串:@Html.DisplayText("IsApproved"),这样的结果和上面的一致。
除了上面的@Html.DisplayFor(x=>x.IsApproved),Html.EditorFor(x =>x.FirstName),还有Html.Display("FirstName"),,Html.Label("FirstName"),Html.LabelFor(x=> x.FirstName),Html.DisplayText("FirstName"), Html.DisplayTextFor(x =>x.FirstName)可以使用。如果想了解个清楚的话,可以自行尝试。建议大家都使用强类型的表示,可以减少错误。注意上面生成的源文件对应的html:
1.2支架模板
我们已经体验了模板助手的快感,下面就会有个问题,既然他会根据类型来判断要生成的字段,如果我们想看到所有的字段,是不是要一个个打出来呢?肯定可以有更简洁的方法,那就是我们要看的一个支架模板,支架模板有三个DisplayForModel,EditorForModel,LabelForModel。如果我们想显示上面的所有字段,我们就是用一个EditorForModel试试。为了看到生成的checkbox,我把IsApproved还改为布尔型。
是不是比我们原来的方法要快捷呢?快的同时,细心的你肯定会发现上面的问题
A.时间字段变长了,
B.地址字段没有显示出来
C.还显示出了用户ID,通常情况我们不想让用户看到这个东西的
如果你是追求完美的人,那么你一定会先想到
D.显示的lable标签是英文的,我想看到的是中文的
看来支架模板不怎么理想啊,出现了这么多问题。下面我们就来一一解决上面出现的问题。解决问题之前,我们先来引入一个名词,元数据(Metadata)在此你就把他看做描述字段的一些标签吧。然后我们就一一的解决问题
A问题的解决办法就是使用
[DataType(DataType.Date)]
public DateTime BirthDate { get; set; }
在DataType上面按F12,会发现其实一个枚举类型,具体的作用可以看其摘要。还可以设置为其他的类型。在此不一一的说明。
B:问题的解决办法:先分析一下为什么不能显示出地址,因为地址的类型是一个类,不是简单类型,所以我们的Framework为了尊重这个类,没有推断出来其字段的类型,所以只有使用前面的@Html.EditorFor(m=>m.HomeAddress),我们列出address的字段时,才可以去推断。
C:问题有两种解决方法,一种是单纯的为了显示,不用把Id以hidden标签来呈现在html中,一种要对其操作,比如说修改了,我们通常使用id作为标识符,只是把其隐藏起来。
下面分别给出两种解决方法:
1、[ScaffoldColumn(false)]//前端不会显示Hidden
public int PersonId { get; set; }
2、[HiddenInput(DisplayValue=true)]如果是true的话,前端会呈现出值和前端生成的hidden,如果是false的话,前端只生成的hidden
D的解决方法是:使用[Display(name=””)]。接下来看一下显示的结果:
对于元数据,还有个[UIHint("")]没有使用上,其作用是把原来的属性的类型按指定的类型显示,其“”里面可以是Boolean、Collection、Decimal、EmailAddress、HiddenInput、HiddenInput、MultilineText、Object、Password、String、Text、Url。其显示结果根据意思可以看出来,具体的查msdn或参考老A文章,如果不能转化的,会报错,如把一个字符串的转化成Boolean的,会提示无法转化。
除此之外,还有个较为重要的是[MetadataType(typeof(ClassName))]元数据,经常用在ORM方面,由于ORM生成的实体如果修改后,编译之后就会把修改的内容删除了,所以可以使用
这个元数据把两个类合并在一起,互相协同工作。但是前提是要声明类为partial,下面还以人员为例:
假设下面的为orm生成的类:

public partial class Person 
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
public bool IsApproved { get; set; }
public Role Role { get; set; }
}

通过MetadataType数据源把其与PersonMetadataSource类协同工作,主要是让姓多行显示,其代码如下:

    [MetadataType(typeof(PersonMetadataSource))]
    public partial class Person
    {
    }
    public partial class PersonMetadataSource
    {
        [UIHint("MultilineText")]
        public string FirstName { get; set; }
    }

其效果图为:
对于其他两个支架模板用法相似,在此不再赘述。
二、自定义视图模板系统
2.1自定义模板的举例
有时,我们想让指定值显示为自己想要的效果,那么我们应该可以使用自定义视图模板。比如用户角色想出现一个ddl的效果,当然可以使用Html.DropDownListFor,还有另外一种方法是在~/Views/Shared 文件夹新建一个文件EditorTemplates,里面添加一个视图,但是这个是分部视图。如图所示:
注意视图名称一定要和前面要展示的类型相一致,这里是和Role保持一致,因为到时间显示的时间,文件名(类型名)是要作为查找的参数。不一致的话,是查找不到的。把下面的代码贴到刚才创建的文件中:

@using ModelTemplate.Models
@model Role
<select id="Role" name="Role">
    @foreach (Role value in Enum.GetValues(typeof(Role))) {
    <option value="@value" @(Model == value ? "selected=\"selected\"" : "")>@value
    </option>
    }
</select>

运行就可以看到用户角色是使用ddl来显示的了,当然还有个好处就是复用了,其他有使用到该字段的视图也会以ddl来呈现,如果只想在某个controller显示的话,可以使用/Views/<controller>/EditorTemplates,该路径是一个指定的路径,不可以改变的,不能把EditorTemplate改为其他的字母。
下面为用户角色显示的效果图:
2.2使用模板的顺序
善于思考的你,看到上面的例子,肯定会想为什么不使用原来的模板了呢,而使用自定义模板,那么什么时间使用内置模板呢?这里面是有个顺序的。下面稍微列出其顺序:
1.Html.EditorFor(m => m.SomeProperty,"MyTemplate")因为我们已经制定了要使用MyTemplate模板,所以排在最前面。
2.被指定了元数据的模版,如UIHint
3.被指定为元数据的数据类型关联的模版,如DataType特性
4.自定义在EditorTemplates、DisplayTemplates(和EditorTemplate类似)里面的模板
5.最后就是内置的模板
注意:上面的2和3容易弄混。一个是元数据模板,一个是元数据的数据类型。我们可以使用一个例子来说明,包装IsApproved字段。
通过UIHint和DataType以及自定义的模板,来说明,同时使用前两个的话显示的是多行,如果同时使用后两个的话,实现的是密码。其中代码如下:

        [UIHint("MultilineText")]
        [DataType(DataType.Password)]
        public bool IsApproved { get; set; }
@model bool
<select id="Role" name="Role">
    <option  value="@Model" @(Model == true ? "selected=\"selected\"" : "")>通知</option>
    <option value="@(!Model)" @(Model == false ? "selected=\"selected\"" : "")>未通知</option>
</select>

可以自行删减,来观察其效果。除了上面的情况,可能还有其他的情况,具体的遇到了再去研究。
三、理解元数据提供体系
截止到现在,我们已经看了前面的一些例子,是什么在我们上面的例子中为我们默默的服务着呢?前面有提到过元数据,那么是谁创造的元数据呢?答案是DataAnnotationsModelMetadataProvider类。是它来检查和处理我们加载类前面的特性的,然后根据其特性来呈现在view中。可以使用F12来看其定义,会发现其实它是继承了AssociatedMetadataProvider一个抽象类,那么我们可以理解为:AssociatedMetadataProvider为元数据的使用提供了一些方法如创建元数据方法,然后DataAnnotationsModelMetadataProvider类重写了他的方法,最后一些属性或者说是特性是属于元数据的。绕了几个弯,还是使用老A的一张图比较好说明:
看了这张图,比较激动的童鞋肯定想创建一个CustomModelMetadataProvider继承AssociatedMetadataProvider类,那么就让我们试着重写CreateMetadata方法,下面就以我IsApproved为例吧,因为翻译为通知有点不妥,我想把他改为赞同。
在项目下建立一个文件夹,然后添加一个类
代码如下:

 public class CustomModelMetadataProvider : AssociatedMetadataProvider
    {
        protected override ModelMetadata CreateMetadata(
                                            IEnumerable<Attribute> attributes,
                                            Type containerType,
                                            Func<object> modelAccessor,
                                            Type modelType,
                                            string propertyName)
        {
            //ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor,
            //modelType, propertyName);
            ModelMetadata metadata =new ModelMetadata (this, containerType, modelAccessor,
            modelType, propertyName);
            if (propertyName != null && propertyName.Equals("IsApproved"))
            {
                metadata.DisplayName = "赞同";
            }
            return metadata;
        }
    }

最后在global文件中Application_Start()添加初始化代码:
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
发现显示的lable已经改过来了,但是其他添加的元数据不起作用了。如果想让它起作用的话,肯定是要继承DataAnnotationsModelMetadataProvider,然后要实现父类的方法,最后在添加自己的功能,这样就可以看到我们想要的效果了。代码如下:

protected override ModelMetadata CreateMetadata(
                                            IEnumerable<Attribute> attributes,
                                            Type containerType,
                                            Func<object> modelAccessor,
                                            Type modelType,
                                            string propertyName)
        {
            ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor,
            modelType, propertyName);
            //ModelMetadata metadata =new ModelMetadata (this, containerType, modelAccessor,
            //modelType, propertyName);
            if (propertyName != null && propertyName.Equals("IsApproved"))
            {
                metadata.DisplayName = "赞同";
            }
            return metadata;
        }

运行的时间,我们可以在创建元数据的时间设置断点看看各个参数的含义,以更好的理解该类和方法。准备收工了,其他的方法就不在此一一实现。
四、总结:
本文通过简单的实例,来引出了模板助手,再实现了自定义模板,最终说明了模型模板的实现原理。文中可能有些名词叫的不是很准确,一方面是因为自己的E文不好,另一个方面是为了更好的理解,如果有不足的地方请大牛们多多指点。附上文中的代码:源码。该代码使用vs2010编写。
五、参考文献:
1、《pro asp.net mvc3 framework》
2、http://home.cnblogs.com/u/mszhangxuefei/
3、http://www.cnblogs.com/artech/
ASP.NET MVC —— Model之一模型模板的更多相关文章
- ASP.NET MVC学习之模型模板篇
		
一.前言 如果你使用ASP.NET MVC制作后台一定会爱上它的EditorForModal.DisplayForModal和LabelForModal方法,因为这些方法可以将模型直接变成对应的标签, ...
 - ASP.NET MVC Model之二模型绑定
		
Asp.net mvc中的模型绑定,或许大家经常用,但是具体说他是怎么一回事,可能还是会有些陌生,那么,本文就带你理解模型绑定.为了理解模型绑定,本文会先给出其定义,然后对通过比,来得出使用模型绑定的 ...
 - ASP.NET MVC Model元数据(五)
		
ASP.NET MVC Model元数据(五) 前言 在上一篇中我们描述了应用于Model上面的各种用于显示控制的特性类,在本篇中将详细的介绍这些特性类的应用,虽然它们跟Model元数据的直接关系并不 ...
 - ASP.NET MVC Model元数据(四)
		
ASP.NET MVC Model元数据(四) 前言 前面的篇幅讲解了Model元数据生成的过程,并没有对Model元数据生成过程的内部和Model元数据结构的详细解释.看完本篇后将会对Model元数 ...
 - ASP.NET MVC Model元数据(三)
		
ASP.NET MVC Model元数据(三) 前言 在上篇中我们大概的讲解了Model元数据的生成过程,并没有对Model元数据本身和详细的生成过程有所描述,本篇将会对详细的生成过程进行讲解,并且会 ...
 - ASP.NET MVC Model元数据(二)
		
ASP.NET MVC Model元数据(二) 前言 在上篇中,给大家留个对Model元数据的印象,并没有对Model元数据有过多的讲解,而在本篇中也不会对Model元数据的本身来解释,而是针对于它的 ...
 - ASP.NET MVC Model元数据(一)
		
ASP.NET MVC Model元数据(一) 前言 在我初学的时候对Model元数据的概念很模糊,或者说是在大脑中没有它的一个模型,作为小白的我去看网上的一些文章还是两眼一黑啥都看不明白,然后我想退 ...
 - ASP.NET MVC Model元数据
		
ASP.NET MVC Model元数据(三) 前言 在上篇中我们大概的讲解了Model元数据的生成过程,并没有对Model元数据本身和详细的生成过程有所描述,本篇将会对详细的生成过程进行讲解,并且会 ...
 - ASP.NET MVC Model验证(四)
		
ASP.NET MVC Model验证(四) 前言 本篇主要讲解ModelValidatorProvider 和ModelValidator两种类型的自定义实现,前者是Model验证提供程序,而Mod ...
 
随机推荐
- Android开发——断点续传原理以及实现
			
0. 前言 在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件.但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录.本文原创, ...
 - [arm学习]makefile学习总结
			
makefile不仅仅是一个命令的集合体,其中有一些规则是需要理解掌握的. 首先,了解makefile的规则: //-----------格式---------- 目标 : 依赖1,依赖2 (TAP键 ...
 - md5加密,同样的代码得到不同的加密结果(已解决)
			
场景: 开发环境(windows下)调用第三方接口验签通过,发测试环境(linux下)后死活验签通过不了 原因: md5是一项成熟的加密技术,问题应该在代码里,查了查感觉可能是字符编码的问题,导致加签 ...
 - 洛咕 P3706 [SDOI2017]硬币游戏
			
假设f[i]是第i个同学胜利的概率,也就是随机序列第一个匹配到s[i]的概率 假设前面有一个字符串\(S\),(假设无限长但没有匹配),现在往后面要加上第i个串\(s[i]\),这个的概率设为\(P_ ...
 - .NetCore利用BlockingCollection实现简易消息队列
			
前言 消息队列现今的应用场景越来越大,常用的有RabbmitMQ和KafKa. 我们用BlockingCollection来实现简单的消息队列. 实现消息队列 用Vs2017创建一个控制台应用程序.创 ...
 - Asp.Net_优化
			
ASP.NET: 一.返回多个数据集 检查你的访问数据库的代码,看是否存在着要返回多次的请求.每次往返降低了你的应用程序的每秒能够响应请求的次数.通过在单个数据库请求中返回多个结果集,可以减少与数据库 ...
 - tcp ,http .udp
			
三次握手,四次挥手要知道,记住. 计算机协议常见面试题,学会了,记住.会运用.
 - python程序出现No module named '_socket' 解决方法
			
首先看一下这个错误,错误显示没有这个_socket这个模块 看一个简单的程序理解这个错误是怎么出现的 这个程序就是像浏览器发起请求发开一个链接然后关闭,一直循环,运行之后产生这个错误,产生这个错误的原 ...
 - Istio如何使用相同的端口访问网格外服务
			
1.1.背景 写这篇文章的目的是为了说明以下问题:如何使用TCP协议相同的端口访问网格外多个服务? 这是最近直播的时候有一个同学提出的,当时我没有完全明白,“访问多集群” 的意思.后来仔细思考了一下, ...
 - Istio 流量治理功能原理与实战
			
一.负载均衡算法原理与实战 负载均衡算法(load balancing algorithm),定义了几种基本的流量分发方式,在Istio中共有4种标准负载均衡算法. •Round_Robin: 轮询算 ...