.NET MVC4 实训记录之六(利用ModelMetadata实现资源的自主访问)
上一篇我们已经实现自定义资源文件的访问,该篇我们使用它配合ModelMetadata实现资源文件的自主访问。这样做是为了我们能更简单的用MVC原生的方式使用资源文件。由于我的文章旨在记录MVC项目的实现,因此不做框架底层实现方面的讲解(其实考虑到自己的能力,也不能为大家讲解的多么深入。如需要更深入的了解MVC底层实现,请自行搜索。在这里我推荐蒋金楠(Artech)老师的相关博文)。
对于使用EF,我们不得不知道System.ComponentModel.DataAnnotations。DataAnnotations下定义了一系列的Attribute,用于我们的属性字段注解方案。例如DisplayAttribue,用于定义属性所显示的名称的文本信息。RequiredAttribute用于定义属性是否必填,以及必填校验失败后的提示信息。它们是我们最常用的注解属性中的两个,我们一般都使用它们来描述我们的字段在用户界面的显示效果。例如我们在UserProfile定义的UserName属性上引入如下Attribute:
[Column(Order = )]
[Required(ErrorMessage = "The User Name is required!")]
[Display(Name = "Filed: User Name")]
public string UserName { get; set; }
在EditUser.cshtml视图中已如下方式显示该字段:
@using (Html.BeginForm("EditUser", "Account", FormMethod.Post))
{
<p>@Html.LabelFor(p=>p.UserName)</p>
<p>@Html.TextBoxFor(p=>p.UserName)</p> <input type="submit" value="提交" />
}
运行项目并打开该页面,直接点击提交按钮,会得到如下的显示效果:
aaarticlea/png;base64,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" alt="" />
这说明MVC框架已经帮我们将字段注册的UI显示信息打印到当前页面。 可是,我们的项目本身的资源语言未定,或者说,需要多语言支持,这个方案就有些牵强。虽然System.ComponentModel.DataAnnotations已经提供了使用自定义资源的相关定义,但都是针对.resx或者已经进行编译过的资源类。无法使用我们自定义的XML资源文件。对此我们有两种方案对其进行改造,以使用我们自定义的资源文件。它们分别是:自定义CustmorDisplayAttribute,改造MVC框架。第一个方案的使用方式与上面例子相同,只不过我们需要为每个需要显示在页面上的Field都引入自定义的属性注释。后两者则更加便捷,无需对类型定义做出任何改变。在这里,我们不讨论第一种方案。
第二种方案属于“高级方案”,也就是说从设计层面解决这个问题。
针对这种方案,我们先要了解ModelMetadata,以及ModelMetadataProvider。在此仅附上相关的代码,不做深入讨论。
首先,是要定义我们自己的ModelMetadataProvider。
public class AppModelMetadataProvider : CachedDataAnnotationsModelMetadataProvider
{
protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor)
{
CachedDataAnnotationsModelMetadata metadata = base.CreateMetadataFromPrototype(prototype, modelAccessor);
if (metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName))
{
metadata.DisplayName = Resource.GetDisplay(string.Format("{0}.{1}", metadata.ContainerType.Name, metadata.PropertyName));
}
return metadata;
}
}
我们仅重写了CachedDataAnnotationsModelMetadataProvider类型中的CreateMetadataFromPrototype方法,在该方法中,我们将Model(这里的Model并非只是页面定义的强类型,它也可以是强类型的属性。当页面初始化阶段,需要获取当前页面的模型类型。因此,这个时候的Model就是页面绑定的强类型实例,其ContainerType为空。但当页面访问到此类型的属性,例如 p => p.UserName,此时的Model就是UserName,ContainerType则为页面绑定的强类型)的DisplayName属性进行修改。原本DisplayName会返回属性的DisplayAttribute注释中定义的资源内容,现在我们将其修改为从自定义资源文件中获取内容(注意:资源的键要严格按照“容器类型名.属性名”进行定义)。
最后,在Global文件的Application_Start()中添加代码以使用我们自定义的Provider。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth(); Bootstrapper.Initialise(); //初始化IOC容器 ModelMetadataProviders.Current = new AppModelMetadataProvider();
}
在视图文件中,我们修改成利用MVC自定义的HtmlHelper显示字段名。如下:
@using (Html.BeginForm("EditUser", "Account", FormMethod.Post))
{
@Html.ValidationSummary()
<div>
@Html.LabelFor(p => p.UserName)
@Html.TextBoxFor(p => p.UserName)
</div>
<input type="submit" value="提交" />
}
第二种方案已完成。运行项目,观察一下我们的页面变化。然后修改一下资源文件中的内容,资源文件的修改也能被立刻应用,而不需要重新编译。
然后我们在UserProfile类型定义中添加一个Address类型的属性。
[Table("UserProfile")]
public class UserProfile
{
//无关代码省略...... public int? AddressId { get; set; } [ForeignKey("AddressId")]
public Address Address { get; set; }
} [Table("Address")]
public class Address: BaseEntity<int>
{
public string City { get; set; }
}
修改EditUser视图,如下:
@using (Html.BeginForm("EditUser", "Account", FormMethod.Post))
{
@Html.ValidationSummary()
<div>
@Html.LabelFor(p => p.UserName)
@Html.TextBoxFor(p => p.UserName)
@Html.LabelFor(p => p.Address.City)
@Html.TextBoxFor(p => p.Address.City)
</div>
<input type="submit" value="提交" />
}
在资源文件中添加
<resource key="Address.City" value="City"/>
那么运行项目打开页面后,可以看到如下效果:
aaarticlea/png;base64,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" alt="" />
导航属性的自定义资源信息也会被显示。这里就可以看出p => p.Address.City时,Provider中的ContainerType则为Address的实际类型,ModelMetadata中的Model则为实际上要显示的子类型的属性。
问题:
我们通过对ModelMetadata内部的属性进行修改,从而实现自定义资源的使用。这种方式属于高阶应用,有必要深入了解MVC的模型绑定相关的知识。本人在完成这篇文章的时候,耗费了相当长的时间(粗略统计大概有3天时间)。主要是想解决多个同类型属性该如何显示不同的自定义资源。例如在UserProfile类型中定义两个同为Address类型的属性,分别为UserAddress、和CompanyAddress。若同时在页面显示这两个属性的City名称,则显示的内容是相同的,都指向同一个资源:
<resource key="Address.City" value="City"/>。但始终未能在第二种方案下找到适合的解决办法。希望有达人能为在下解惑,不胜感激!!!
下期预告:第三中方案解决同类型导航属性显示不同的自定义资源。
.NET MVC4 实训记录之六(利用ModelMetadata实现资源的自主访问)的更多相关文章
- .NET MVC4 实训记录之七(实现资源的自主访问后续)
我们在上一篇中讨论了如何利用ModelMetadata实现国际化资源文件访问,但也留下了一些问题,即:如何利用ModelMetadata实现相同类型的属性信息的个性化资源显示.本人没有找到合适的方案, ...
- .NET MVC4 实训记录之四(Unit of work + Repository)
今日后开启进阶模式! 谈到MVC与EntityFramework,则不得不说一说事务与仓储(Unit of work + Repository). 仓储(Repository):领域对象集合.用于操作 ...
- .NET MVC4 实训记录之二(扩展WebSecurity模型下的UserProfile表)
使用VS2013创建MVC4项目后,自动生成的代码中默认使用WebSecurity模型创建用户管理,生成以下数据库:
- .NET MVC4 实训记录之一(引入Unity3.0 Ioc框架)
一直在做维护项目,没有机会接触完整的架构,于是自学.NET MVC.自今日起,将自学的过程.遇到的问题以及解决方案记录下来. 在WebApp项目中右键,使用NuGet引入Unity3.0.
- .NET MVC4 实训记录之五(访问自定义资源文件)
.Net平台下工作好几年了,资源文件么,大多数使用的是.resx文件.它是个好东西,很容易上手,工作效率高,性能稳定.使用.resx文件,会在编译期动态生成已文件名命名的静态类,因此它的访问速度当然是 ...
- .NET MVC4 实训记录之三(EntityFramework 与枚举)
EntityFramework对枚举的引入是从版本5开始的(如果没有记错的话).枚举可以很大程度上提高对程序的可读性.那么在EntityFramework的CodeFirst模式下,如何使用枚举呢?答 ...
- 实训任务05 MapReduce获取成绩表的最高分记录
实训任务05 MapReduce获取成绩表的最高分记录 实训1:统计用户纺问次数 任务描述: 统计用户在2016年度每个自然日的总访问次数.原始数据文件中提供了用户名称与访问日期.这个任务就是要获取 ...
- UML基础与Rose建模实训教程
目 录 第1章 初识UML. 1 1.1 初识UML用例图... 1 1.2 初识UML类图... 3 第2章 Rational Rose工具... 6 2.1 安装与配置Rational Ro ...
- vi, Java, Ant, Junit自学报告 - 实训week1
vi, Java, Ant, Junit自学报告 2017软件工程实训 15331023 陈康怡 vi Vi是linux系统的标准文本编辑器,采用指令的方式进行操作,此处仅记录部分常用的指令. vi模 ...
随机推荐
- Hadoop Java Hdfs API
1. 在本地文件系统生成一个文本文件,,读入文件,将其第101-120字节的内容写入HDFS成为一个新文件2. 在HDFS中生成文本文件,读入这个文件,将其第101-120字节的内容写入本地文件系统成 ...
- MLAPP——概率机器学习知识汇总
<机器学习>课程使用Kevin P. Murphy图书<Machine Learning A Probabilistic Perspective>本英语教材,本书从一个独特的数 ...
- 初识Java——(Java学习笔记一)
冯诺依曼体系结构 JAVA核心优势:跨平台---通过JVM(java虚拟机)来实现 JVM:Java虚拟机的一种规范 标示符:只能以下划线.美元符号($).字母.数字组成,不能以数字开 ...
- Hibernate各保存方法之间的差 (save,persist,update,saveOrUpdte,merge,flush,lock)等一下
hibernate保存 hibernate要保存的目的是提供一个方法,多.它们之间有许多不同之处,点击此处详细说明.使得差: 一.预赛: 在所有.阐释.供hibernate,,transient.p ...
- jQuery本身方法($.each,$.map,$.contains,$ajax)
常用工具方法 (1)$.trim $.trim方法用于移除字符串头部和尾部多余的空格. $.trim(' Hello ') // Hello (2)$.contains $.contains方法返回一 ...
- 《java系统性能调优》--1.发现瓶颈
性能啊!性能! 之所以想写写性能调优,也是有感于我们的项目,我们採用一些手段使得系统性能上升了一个台阶,总是须要把这点经验沉淀一下.随着工作的深入,关于系统性能的事肯定还有非常多,也算是通过这个系列文 ...
- LeetCode:same_tree题解
一. 题目: 给定两个二叉树,编写一个函数来检查它们是否相等或为空树.假设两个二叉树被觉得是相等的,那么它们在结构上是同样的,而且随意节点具有同样的值. 二. 分析 非常easy的题目 ...
- struts2 模型分配问题和延迟加载问题
傅型号值问题: 首先须要说明的是:Action在请求到达ActionProxy时已经创建出来了,而且对应的创建了一个值栈. 在拦截器到达之前这个图片已经OK了.Action已经创建.并且压入了值栈vs ...
- HTML5 Canvas简简单单实现手机九宫格手势密码解锁
原文:HTML5 Canvas简简单单实现手机九宫格手势密码解锁 早上花了一个半小时写了一个基于HTML Canvas的手势解锁,主要是为了好玩,可能以后会用到. 思路:根据配置计算出九个点的位置,存 ...
- php 基础算法(用*表示金字塔)通过hash 比較两个数组同样的数
当作为一名php 程序猿,每天总与数据库打交道,做着最底层的程序猿的工作,開始着手研究一些算法,希望自己能在计算机的道路上走的更远.事实上我更喜欢管理,希望自己能作为一个卓越的管理者,但并不影响我对技 ...