.NET MVC4 实训记录之七(实现资源的自主访问后续)

  我们在上一篇中讨论了如何利用ModelMetadata实现国际化资源文件访问,但也留下了一些问题,即:如何利用ModelMetadata实现相同类型的属性信息的个性化资源显示。本人没有找到合适的方案,期待着高人的指点。

  本章,介绍第三种资源访问方案,用于解决上述问题(该方案并非从设计角度解决问题)。

  首先,描述下我们的问题。

  第一步,在UserProfile类型中添加两个Address类型的属性:

 1         #region 用户住址信息
 2
 3         public int? UserAddressId { get; set; }
 4
 5         [ForeignKey("UserAddressId")]
 6         public Address UserAddress { get; set; }
 7
 8         #endregion
 9
10
11         #region 用户公司地址信息
12
13         public int? CompanyAddressId { get; set; }
14
15         [ForeignKey("CompanyAddressId")]
16         public Address CompanyAddress { get; set; }
17
18         #endregion

  第二步,在EditUser视图中展示这两个属性的City信息:

 1 @using (Html.BeginForm("EditUser", "Account", FormMethod.Post))
 2 {
 3     @Html.ValidationSummary()
 4     <div>
 5         @Html.LabelFor(p => p.UserName)
 6         @Html.TextBoxFor(p => p.UserName)
 7
 8         @Html.LabelFor(p => p.UserAddress.City)
 9         @Html.TextBoxFor(p => p.UserAddress.City)
10
11         @Html.LabelFor(p => p.CompanyAddress.City)
12         @Html.TextBoxFor(p => p.CompanyAddress.City)
13     </div>
14     <input type="submit" value="提交" />
15 }

  第三步,在资源文件中添加相应的资源:

1   <resource key="UserProfile.UserAddress" value="User Address "/>
2   <resource key="UserProfile.UserAddress.City" value="User City "/>
3   <resource key="UserProfile.CompanyAddress" value="Company Address "/>
4   <resource key="UserProfile.CompanyAddress.City" value="Company City "/>

  第四步,使用自定义ModelMetadataProvider绑定显示信息:参考上一篇博文。

  运行项目打开页面,看到内容如下:

  如图所示,自定义的资源信息未能如愿显示在页面上。这是由于在Provider中,此时的Container并非是当前页面绑定的强类型,而是当前属性的持有者的类型。

  如上所示,构造出的资源键值为"Address.City"(针对这两个属性,我们会得到同样的结果),因此没有能够显示我们预想的信息。令人难过的是,虽然我们可以通过查看modelAccessor参数得知当前访问的属性来源,但仍然无法在运行期获取这个来源(至少我还没有找到方法)。

  就是它困扰了我很久,明明可以看到,但就是无法获取。索性换个方式,使用HtmlHelper扩展方法。

  扩展方法是一个很好的东西,它是framework3之后才引入的。其实现我们随处可见,也常常会使用。例如对集合进行过滤使用Collection.Where,或者排序OrderBy等,都是在使用Linq的扩展方法。它的好处显而易见,可以不使用继承,而直接对原有类型实现功能的扩展。OK,看看我们如何实现。

  首先,需要一个静态类,一个静态方法(扩展方法是在一个静态类中定义一个静态方法,该方法将需要进行扩展的类型作为参数,并使用this关键字声明是对它的扩展)。

 1 namespace MvcApp.Helpers
 2 {
 3
 4     public static class HtmlHelperExt
 5     {
 6         public static MvcHtmlString LabelForProperty<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
 7         {
 8             return LabelForProperty(html, expression, null);
 9         }
10
11         public static MvcHtmlString LabelForProperty<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
12         {
13             string navPath = expression.Body.ToString().TrimStart(expression.Parameters.First().ToString().ToArray()).TrimStart(‘.‘);
14             string res = Resource.GetDisplay(string.Format("{0}.{1}", typeof(TModel).Name, navPath));
15             return LabelExtensions.Label(html, navPath, res ?? navPath, htmlAttributes);
16         }
17         public static MvcHtmlString LabelForProperty<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
18         {
19             string navPath = expression.Body.ToString().TrimStart(expression.Parameters.First().ToString().ToArray()).TrimStart(‘.‘);
20             string res = Resource.GetDisplay(string.Format("{0}.{1}", typeof(TModel).Name, navPath));
21             return LabelExtensions.Label(html, navPath, res ?? navPath, htmlAttributes);
22         }
23     }
24 }

  在上面的代码示例中,我们实现了三个扩展方法,它们分别对应与原有的方法:

LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression);

LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes);

LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes);

  在实现扩展方法时,我们首先通过expression.Body.ToString()获取了当前属性的路径,对于p=>p.UserAddress.City,将返回它的字符串形式"p.UserAddress.City"。然后,再将字符串中的形参表达式 p 替换成当前类型(也就是页面绑定的类型)的名称,则生成字符串"UserProfile.UserAddress.City"。这个字符串符合我们对资源键值定义的格式:"类型名称.属性名"(次例子中,属性名实际上是"UserAddress.City",而非"City")。当获取到这个资源的键,通过资源访问器获取到资源内容,调用LabelExtensions类型的Label(this HtmlHelper html, string expression, string labelText, IDictionary<string, object> htmlAttributes)方法,构造一个label标签。最后,我们需要导入扩展方法实现的名空间,这样在Razor视图中才能使用这些扩展方法。只需在Views文件夹下的Web.config(而不是根目录下的Web.config)文件中添加扩展实现的名空间即可:

 1 <system.web.webPages.razor>
 2     <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
 3     <pages pageBaseType="System.Web.Mvc.WebViewPage">
 4       <namespaces>
 5         ......
10         <add namespace="MvcApp.Helpers" />
11       </namespaces>
12     </pages>
13   </system.web.webPages.razor>

  至此所有准备工作都已经完成,然后是修改我们的视图,使用新的扩展方法:

 1 @using (Html.BeginForm("EditUser", "Account", FormMethod.Post))
 2 {
 3     @Html.ValidationSummary()
 4     <div>
 5         @Html.LabelForProperty(p => p.UserName)
 6         @Html.TextBoxFor(p => p.UserName)
 7
 8         @Html.LabelForProperty(p => p.UserAddress.City)
 9         @Html.TextBoxFor(p => p.UserAddress.City)
10
11         @Html.LabelForProperty(p => p.CompanyAddress.City)
12         @Html.TextBoxFor(p => p.CompanyAddress.City)
13     </div>
14     <input type="submit" value="提交" />
15 }

  运行页面得到如下结果:

  值得一提的是,该方案与ModelMetadata没有任何冲突,因为该扩展方未曾调用ModelMetadataProvider。也就是说,这两种方案可以并存。当我们的页面模型类型中不存在多个相同类型的属性需要显示在界面,则可以直接使用LabelFor方法,使用Provider重置显示的内容;而当我们有多个相同类型的导航属性,并且需要将其在页面展示时进行区分时,可使用我们新增的扩展方法(实际上,任何属性的显示,都可以使用这种扩展方法,而不仅局限于此例。如LableForPropery(p => p.UserName))。

  这样自定义资源的自主访问便得到了完善。

  其实大家注意到,我们此扩展方法实际上就是在Razor视图中调用Html.Lable(string expression, string labelText)。但不同点在于,我们将硬编码的labelText资源键值改变为动态生成资源键值,这种书写方式是值得提倡的。试想假如有一天,资源key值方案改变,例如所有key值前面会附加一个Application名称作为前缀,用于区分不同的应用,那么硬编码情况下,我们需要进行大量的修改。而是用扩展方法,我们仅需要修改扩展方法,追加前缀就可以一次性解决,和乐而不为之呢?

时间: 2024-10-10 06:49:48

.NET MVC4 实训记录之七(实现资源的自主访问后续)的相关文章

.NET MVC4 实训记录之五(访问自定义资源文件)

.Net平台下工作好几年了,资源文件么,大多数使用的是.resx文件.它是个好东西,很容易上手,工作效率高,性能稳定.使用.resx文件,会在编译期动态生成已文件名命名的静态类,因此它的访问速度当然是最快的.但是它也有个最大的缺点,就是修改资源文件后,项目必须重新编译,否则修改的资源不能被识别.这对于维护期的工作来讲,非常麻烦.尤其是已经上线的项目,即使是修改一个title的显示,也需要停掉项目.由于本人做了好几年的维护,应该是从工作到现在,一直没有间断过的做维护项目,因此深受其害!必须找到一个

.NET MVC4 实训记录之四(Unit of work + Repository)

今日后开启进阶模式! 谈到MVC与EntityFramework,则不得不说一说事务与仓储(Unit of work + Repository). 仓储(Repository):领域对象集合.用于操作领域对象与数据库上下文(DbContext)的交互(在此不得不说一声,领域对象和数据库表对象还是有区别的.领域对象实际上是一组有业务关系的数据库对象的抽象.最简单的形式就是主表.关系表在同一个领域对象中进行定义.例如我们前几章看到的UserProfile,它即定义了用户信息,又定义了用户角色关系信息

.NET MVC4 实训记录之一(引入Unity3.0 Ioc框架)

一直在做维护项目,没有机会接触完整的架构,于是自学.NET MVC.自今日起,将自学的过程.遇到的问题以及解决方案记录下来. 在WebApp项目中右键,使用NuGet引入Unity3.0. 引入后项目目录下会自动生成一个Bootstrapper.cs文件. 手动在WebApp根目录下创建IOC.config文件,用于配置IOC规则.(注意:之前看网上教程中的配置文 件,<typeAliases></typeAliases>节点和<unity></unity>

.NET MVC4 实训记录之二(扩展WebSecurity模型下的UserProfile表)

使用VS2013创建MVC4项目后,自动生成的代码中默认使用WebSecurity模型创建用户管理,生成以下数据库: 用户信息只有ID和UserName,角色信息也只有两个基础字段.通常情况下这样的数据表不能满足我们的需求,因此对其进行扩展. 首先定义自己的用户信息.角色信息结构. 1 [Table("UserProfile")] 2 public class UserProfile 3 { 4 [Key] 5 [DatabaseGenerated(DatabaseGeneratedO

.NET MVC4 实训记录之三(EntityFramework 与枚举)

EntityFramework对枚举的引入是从版本5开始的(如果没有记错的话).枚举可以很大程度上提高对程序的可读性.那么在EntityFramework的CodeFirst模式下,如何使用枚举呢?答案很简单:还是那么用! 看似废话,其实不然,看下面(修改上一篇中用户信息定义): /// <summary> /// 性别枚举 /// </summary> public enum Gender { Male, Female } public class UserProfile { [

.NET MVC4 实训记录之六(利用ModelMetadata实现资源的自主访问)

上一篇我们已经实现自定义资源文件的访问,该篇我们使用它配合ModelMetadata实现资源文件的自主访问.这样做是为了我们能更简单的用MVC原生的方式使用资源文件.由于我的文章旨在记录MVC项目的实现,因此不做框架底层实现方面的讲解(其实考虑到自己的能力,也不能为大家讲解的多么深入.如需要更深入的了解MVC底层实现,请自行搜索.在这里我推荐蒋金楠(Artech)老师的相关博文). 对于使用EF,我们不得不知道System.ComponentModel.DataAnnotations.DataA

实训任务05 MapReduce获取成绩表的最高分记录

实训任务05  MapReduce获取成绩表的最高分记录 实训1:统计用户纺问次数 任务描述: 统计用户在2016年度每个自然日的总访问次数.原始数据文件中提供了用户名称与访问日期.这个任务就是要获取以每个自然日为单位的所有用户访问次数的累加值.如果通过MapReduce编程实现这个任务,首先要考虑的是,Mapper与Reducer各自的处理逻辑是怎样的:然后根据处理逻辑编写出核心代码:最后在Eclipse中编写完整代码,编译打包后提交给集群运行. 分析思路和逻辑 (1)       输入/输出

&lt;实训|第四天&gt;Linux下的vim你真的掌握了吗?附上ftp远程命令上传。

期待已久的linux运维.oracle"培训班"终于开班了,我从已经开始长期四个半月的linux运维.oracle培训,每天白天我会好好学习,晚上回来我会努力更新教程,包括今天学到了什么知识点,用了什么方法,使用了什么工具,工具在哪里共享着等等,努力做到让每一位感兴趣的.关注我的.没关注我的老司机都能在自己闲暇的时光学到一点额外的知识,指不定那一天你就用上了少许呢! ------------------------------------------------------------

山西高平地域文化导入美术设计实训项目的实践

地域文化,是指某个地域的人们在特定的范围内,在自然环境的基础上,在长期的生产生活中创造的.人类活动的产物.山西高平是神农炎帝的故里,长平之战的发生地,是一个有着很深文化积淀的新兴城市,行政区域是国土面积的万分之一,却浓缩了中国五千年文化的精髓.本文通过对山西高平丰富的历史文化资源的调查研究,探讨如何将地域文化元素导入中职美术设计专业的实训项目教学过程,论证典型地域文化元素在中职美术设计教育中的独特作用,并以此作为教学实训素材,依托带有典型地域文化特征的企业真实案例,结合教育教学实践,探索中职美术