WebForm 对 MVC 说:能否借你的UpdateModel方法来用用?

背景

ASP.NET MVC的Controller有个很不错的方法:UpdataModel (相对应的还有TryUpdateModel)。它能够把提交的数据(Form, QueryString, RouteData)自动更新到实体,例如:

如果提交的数据键值与Customer的属性相对应,就可以实现对Customer的属性进行更新。而一般在ASP.NET WebForm中,我们可能要写上很多类似这样的语句(在实际开发中每个页面可不止两三个赋值语句):

据我所知,有不少企业早就利用反射开发出像UpdateModel的方法,基本上符合中小型项目的需要,毕竟对于由UpdateModel带来的一点性能损失,远远比不上由于没有UpdateModel而导致需要写一堆赋值语句所带来的损失。但是,绝大多数企业的程序员还是一直在写这样的赋值语句,即使已经受尽了这种类似“机器人在打字”的折磨(因为可以夸张的说,实在看不出当写这些赋值语句时需要调用人类大脑哪些神经,所以说类似“机器人在打字”,大脑处于麻木状态),或许有些程序员喜欢这样,也乐此不疲,那么我只能说“佩服”。我是一个很很很“懒惰”的人,不喜欢干些类似“重复”性的活,我总是想着法子怎么写出通用的代码去让我能够在以后可以“偷懒”,就算是写一个小小的代码生成工具也好。 刚才说过“有不少企业早就利用反射开发出像UpdateModel的方法”,遗憾的是直到现在,我都是耳闻而没有真正看到WebForm下类似MVC的UpdateModel(注:本文说提及的WebForm 和MVC 都是ASP.NET的范畴,而UpdateModel虽然实际上只是Controller里的一个方法,但本文它还作为数据绑定机制的代名词,只是不想打太多文绉绉的名称,以防自己都看不懂)。

前面已经提到,UpdateModel其本质无非是把提交的数据(NameValueCollection 类型)转化成实体,相信每个用过反射的人都知道怎么写。但是,想写好可不简单。这里的“好”是至少包含以下几点:可扩展性(是否可以通过override每个方法,或者继承某个基类或接口来实现修改数据绑定的机制),泛化(是否可以支持复杂类型的实体,例如泛型集合),健壮性等等。

UpdateModel 正式登场

说了这么多废话,其实是想引出下面的话题: 既然MVC已经有一个优秀的UpdateModel,而且它同样是ASP.NET下的好孩子,何不“借”来给WebForm用用?说借就借,MVC挺大方的,还再三叮嘱:当初没想到老兄会对我的UpdateModel感兴趣啊,所以都是写成protected internal的访问形式了,您觉得没问题就改改凑合凑合吧。于是MVC随手掏出以下的C#文件:

没有IController这个接口,Controller怎么“活”呀?没错,WebForm只是想借“UpdateModel”,可没打算背着其他多余的东西,所以凡是与UpdateModel无关的东西都被干掉了,包括Controller后面一大堆的接口。另外,还需要清除以下的东东(如果需要添加代码肯定把我累倒,幸好只是做清除动作,暗喜中…):

1. ControllerBase 去掉IController接口,当然把与之相关的Execute,ExecuteCore方法去掉,甚至去掉Initialize方法;TempData,ViewData等等都不要了(题外话:其实TempData也是挺不错的,但是WebForm什么场景下能用到呢,苦想半天脑海中也只有零零星星几个场景,暂忽略之)。

2. ControllerContext 只保留HttpContext 和 Controller

3. Controller 最惨, 几乎减掉一半重量,只剩下Binders ,ModelState 和UpdateModel, TryUpdateModel 的一堆重载方法(如果你不需要记录数据绑定错误而带来的异常,可把ModelState也请走)。

4. 其他的改动已经忘了,印象中改动较大的就是把与RouteData, ViewData 相关的东东全部请走。

万事俱备,只欠东风。谁是东风呢?其实是为了让WebForm 中调用UpdateModel的写法与MVC一致,这里“东风”就是“扩展方法”!

(经过一番测试, UpdateModel如果由空值string.Empty向数字或日期类型转化,基本上100%都是抛异常的,但这不影响Model其他属性的更新,你也可以用TryUpdateModel)

我们应该是时候写一个Hello UpdateModel 来测试一番了:

(上一篇文章里,不少朋友抱怨我的VS 的IDE样式太难看,大家先凑合看一眼,反正都是一些无关紧要的代码)

后台测试代码:

最后点击Submit按钮成功输出:Name: Bruce Lee, Age: 123

而UpdateModel的其他重载方法都可以像MVC那样正常使用,至于QueryString更新到Model的例子,有兴趣的朋友可以自己试试,这里不一一列举。

这里想引出另外一个问题: 在WebForm里,有多少人在对控件命名时是不写前缀的呢?即实际开发中,一般控件都类似这样写:“txtName”,“txtAge”, UpdateModel可以应付吗?可能有些朋友开始失望了,也有甚者可能会说:很多人都明白MVC不该用WebForm的服务端控件,那么同样WebForm也不要去想着从MVC那里借鉴什么了。暂时来说前者我非常认可,后者我可不觉得有什么不可借鉴的。如果因为这个前缀而让UpdateModel就此划上句号的话,那么就不会是我写这篇文章的初衷。

俗话说,入乡随俗。MVC的UpdateModel来到WebForm的世界就要做点变通,顺应WebForm的一贯做法。以上提到,我对好代码的其中一个观点是易于扩展。前面的代码已经被我“砍”得“惨不忍睹”了,不想再为此而大动手术,我们就来个扩展好不好?(注:并非指.Net扩展方法,大家可以理解为拓展)

ValueProviderDictionary里有一个方法PopulateDictionary,其主要作用就是把Form,QueryString等数据统一添加到内置的字典里,而作为UpdateModel的ValueProvider。聪明的你可能已经想到,由于控件ID作为字典的key, 所以只要改动此方法,在添加数据到内置字典前去掉前缀再作为key不就成功了么?因为ValueProviderDictionary是作为默认的ValueProvider, 我不想改动它,所以另外写了一个ValueProvider: PrefixableValueProvider,其意思是:支持前缀的ValueProvider。

首先定义可能出现的前缀枚举类型:1. None,2. Camel,3. Three。1 和 3 容易理解,这里说说Camel,从字面上用Camel可能不是很妥当,详细请参考。这里是指控件的前缀都是小写字母,而随后的英文单词首字母大写。例如:txtName,lblCustomer,这是多数企业使用的控件命名规范。

PrefixableValueProvider同样实现“IDictionary<string,ValueProviderResult>”接口,里面只有PopulateDictionary方法与ValueProviderDictionary不同。 对于Camel规则,因为前缀都是小写字母,所以只要找到第一个非小写字母,就从这个字母开始截取字符串作为key即可。下面给出了一个简单的实现代码,大家可以完全不用看,以免被我的低效率和考虑不周全的代码给误导。

上面的代码考虑到这样的场景:当控件ID有下面几个:“Name”, “txtName”…,如果“Name”先被加入,而当后面发现有“txtName”,那么就会移除“Name”而使用“txtName”的值;如果先发现“txtName”,而后面不管有叫“Name”的控件还是叫“Name”的查询字符串都不会覆盖前者。

除此之外,PrefixableValueProvider百分之九十九的代码都是跟ValueProviderDictionary一样。可能有人问,这不煞费苦心吗?我也想继承ValueProviderDictionary,而override它的PopulateDictionary方法呀,谁知道它原来是private的。不过等真正用于实际开发时,我肯定把private换成protected virtual,然后在PrefixableValueProvider中override它。

瞎扯的太远差点忘了说怎么使用这个PrefixableValueProvider了,其实就是要指定Controller的ValueProvider是PrefixableValueProvider就可以:

把前面例子的控件ID改成“txtName”和“txtAge”测试就可以了,这里不详细写了。

缺点

任何圣人都有缺点,何况只是一个UpdateModel。它的缺点是:

1. 由于控件ID需要与Model符合某种约束,所以在为控件命名时就不能那么“自由”了。

2. 当页面要提交的数据跟Model不是那么一一对应时,可能需要另外处理。

代码

上面的贴图可能真的由于IDE的样式和图片压缩的原因而惨不忍睹了,可以下载本文完整的源代码:UpdateModel1.0.rar

UpdateModel方法的更多相关文章

  1. iview Checkbox 多选框 单个的时候 如果需要change 以后进行赋值 就要用value 不要用v-modal 然后用updateModel 方法

    noSuchSituationSetFalse () { this.noSuchSituationOne = false this.$refs.noSuchSituationRef.updateMod ...

  2. ASP.NET MVC 在控制器中接收视图表单POST过来的数据方法

    方法一:通过Request.Form [HttpPost]        public ActionResult Test()        {            string id=Reques ...

  3. ASP.NET MVC系列:Model

    1. Model任务 Model负责通过数据库.AD(Active Directory).Web Service及其他方式获取数据,以及将用户输入的数据保存到数据库.AD.Web Service等中. ...

  4. [ASP.NET MVC 小牛之路]15 - Model Binding

    Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...

  5. 7 天玩转 ASP.NET MVC — 第 3 天

    目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 我们假定你在开始学习时已经阅读了前两天的学习内容.在第 2 天我们完成了关于显示 Employee ...

  6. MVC Create

    本文介绍如何在MVC里往数据库中插入新的记录. 这里用到的数据表如下: Employees Step 1: 在Control文件里加入method public ActionResult Create ...

  7. ASP.NET MVC 入门8、ModelState与数据验证

    原帖地址:http://www.cnblogs.com/QLeelulu/archive/2008/10/08/1305962.html ViewData有一个ModelState的属性,这是一个类型 ...

  8. MVC3 ModelBinder

    1.Model Binder从哪些地方获取数据(找到数据后会停止继续寻找) MVC 框架内置默认的 Model Binder 是 DefaultModelBinder 类.当 Action Invok ...

  9. Asp.Net MVC 模型(使用Entity Framework创建模型类) - Part.1

    这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework来创建数据访问类.这篇教程假设你事先对Microsoft Entity Fram ...

随机推荐

  1. Java并发编程之ConcurrentHashMap(转)

    ConcurrentHashMap ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法.Concurrent ...

  2. Windows Phone 8 应用内截图

    WriteableBitmap wb = new WriteableBitmap(this.LayoutRoot, new MatrixTransform()); //wb.Render(this.L ...

  3. 依据Uri获得sd卡图片

    <pre name="code" class="java">String mBigImage = URLEncoder.encode(mImageU ...

  4. 中介模式和学习日记Effective C++

    中介模式(Mediator):利用中介对象来封装一组对象交互.中保使对象并不需要显式地相互引用,使得松耦合,的交互. (1).中介者模式非常easy在系统中应用,也非常easy在系统中误用.当系统出现 ...

  5. Gallatin(大陆版)Office365中Exchange Online混合部署功能已经能够使用了

    经过測试,Exchange混合部署已经能够使用了 前置条件: 本机至少须要一台Exchange Server 2013作为混合部署server 须要一个公网域名 domian.com,能够和内部域名不 ...

  6. iOS发展系列II - UILabel 使用摘要

    // 初始化标签 UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 100, 300, 150)]; // 设置标签文字 l ...

  7. oracle11g ASM(修复损坏的磁盘组头asm修复2)

    --编KFED [oracle@rac2 lib]$cd $ORACLE_HOME/rdbms/lib [oracle@rac2 lib]$ pwd /u01/app/oracle/product/1 ...

  8. react.js 从零开始(三)JSX 语法及特点介绍

    什么是jsx? jsx = JavaScript + xml jsx 是一种 Ecmascript 的一种新标准. jsx 是一种 带有结构性的语法. jsx 的特点: 1.类xml语法易于理解. 2 ...

  9. 一个数据表对象(NSManagedObject)加入排序

    eg:数据库表对象 @interface Meditation : NSManagedObject @property (nonatomic, retain) NSString * order;//用 ...

  10. 【高德地图API】从零开始学高德JS API(五)路线规划——驾车|公交|步行

    原文:[高德地图API]从零开始学高德JS API(五)路线规划——驾车|公交|步行 先来看两个问题:路线规划与导航有什么区别?步行导航与驾车导航有什么区别? 回答: 1.路线规划,指的是为用户提供3 ...