UpdateModel方法
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方法的更多相关文章
- iview Checkbox 多选框 单个的时候 如果需要change 以后进行赋值 就要用value 不要用v-modal 然后用updateModel 方法
		
noSuchSituationSetFalse () { this.noSuchSituationOne = false this.$refs.noSuchSituationRef.updateMod ...
 - ASP.NET MVC 在控制器中接收视图表单POST过来的数据方法
		
方法一:通过Request.Form [HttpPost] public ActionResult Test() { string id=Reques ...
 - ASP.NET MVC系列:Model
		
1. Model任务 Model负责通过数据库.AD(Active Directory).Web Service及其他方式获取数据,以及将用户输入的数据保存到数据库.AD.Web Service等中. ...
 - [ASP.NET MVC 小牛之路]15 - Model Binding
		
Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...
 - 7 天玩转 ASP.NET MVC — 第 3 天
		
目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 我们假定你在开始学习时已经阅读了前两天的学习内容.在第 2 天我们完成了关于显示 Employee ...
 - MVC Create
		
本文介绍如何在MVC里往数据库中插入新的记录. 这里用到的数据表如下: Employees Step 1: 在Control文件里加入method public ActionResult Create ...
 - ASP.NET MVC 入门8、ModelState与数据验证
		
原帖地址:http://www.cnblogs.com/QLeelulu/archive/2008/10/08/1305962.html ViewData有一个ModelState的属性,这是一个类型 ...
 - MVC3 ModelBinder
		
1.Model Binder从哪些地方获取数据(找到数据后会停止继续寻找) MVC 框架内置默认的 Model Binder 是 DefaultModelBinder 类.当 Action Invok ...
 - Asp.Net MVC 模型(使用Entity Framework创建模型类) - Part.1
		
这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework来创建数据访问类.这篇教程假设你事先对Microsoft Entity Fram ...
 
随机推荐
- web 环境项目(intellj部署的tomcat) 重启时报 Exception in thread "HouseKeeper" java.lang.NullPointerException (转)
			
Exception in thread "HouseKeeper" java.lang.NullPointerException at org.logicalcobwebs.pro ...
 - 动态生成Zip
			
动态生成Zip文档 通过前面一篇烂文的介绍,大伙儿知道,ZipArchive类表示一个zip文档实例,除了用上一篇文章中所列的方法来读写zip文件外,还可以直接通过ZipArchive类,动态生成 ...
 - NSIS:实现程序窗口逐渐透明的渐入渐出效果
			
原文NSIS:实现程序窗口逐渐透明的渐入渐出效果 需要修改版的插件(支持timer功能): MUI:InstallOptions.dll MUI2:nsDialogs.dll 以及system插件,( ...
 - 每天收获一点点------Hadoop基本介绍与安装配置
			
一.Hadoop的发展历史 说到Hadoop的起源,不得不说到一个传奇的IT公司—全球IT技术的引领者Google.Google(自称)为云计算概念的提出者,在自身多年的搜索引擎业务中构建了突破性的G ...
 - Android开发之异步具体解释(二)之AsyncTask
			
请尊重他人的劳动成果,转载请注明出处:Android开发之异步具体解释(二)之AsyncTask http://blog.csdn.net/fengyuzhengfan/article/details ...
 - JAVA学习第六十二课 — TCP协议练习
			
通过练习掌握TCP在进行传输过程中的问题 练习1:创建一个英文大写转换server client输入字母数据,发送给服务端,服务端收到后显示到控制台,并将该数据转成大写返回client,知道clien ...
 - Android - 采用ApiDemos得知Android开展
			
采用ApiDemos得知Android开展 本文地址: http://blog.csdn.net/caroline_wendy 位置: android-sdk/samples/android-20/l ...
 - CentOS6.5查看一port执行状态
			
netstat -nap | grep 22 版权声明:本文博主原创文章,博客,未经同意不得转载.
 - ABP领域层——工作单元(Unit Of work)
			
ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...
 - SQL随着子查询结果更新多个字段
			
笔者:iamlasong 要求:表格内容需要改变,在临时表中内容的变化,使用SQL官方声明更新表若干领域. 假设更新一个字段,直接用字段名=子查询就能够了,多个字段更新,将字段在括号里并列写出就可以, ...