在上篇中.我们分析了解了ModelBinder而本文将在实例中添加自己的ModelBinder,以便获得我们期望中的效果.本次我们通过一个简单的Demo来对全站的ModelBinder做一个解决方案.
首先给出工程文件图:
注意一点,本例中,我的目标是给整站的Entity对象实现一个通用的,可扩展的ModelBinder引擎.由于我们要让实体对象和url字符串之间转换,为了方便,可以定义相应的方法来实现,因此定义了IEntity接口,该接口定义如下:
解释下,Fill方法是将对应的字符串转换成对象.ToString方法是将对象转换成特定的字符串.也就是说,只要某个对象实现了该接口,我们就可以将该对象方便的从url字符串和对象之间转换.这正是我们要达到的目标.另一个方式是让IEntity要实现IConvertible,不过IConvertible中方法实在太多,偷懒就不用它了.
然后,在项目中,BaseEntity是一个抽象类,它只实现了IEntity中关于对象和字符串转换的方法.为了通用性,这儿用反射来实现.Fill方法实现:
该方法中我们假定传入的entityString满足标准的Url参数形式,及”xxx=xxx&xxx2=xxx2”的模式.然后解析字符串.转换类型并填充.有一点要注意,这儿将string转换成实体的内置对象的时候使用Convert方法,因此,如果继承字该类的实体中有自定义类型,必须对该类实现IConvertible接口,至少要能在string和该对象之间转换.
然后再实现了ToString方法:
ToString方法中,如果没有提供modelName,则直接转换为”属性名=属性数据&属性名2=属性数据2”的样式,如果提供了modelName,则转换为”实例名_属性名=属性数据&实例名_属性名2=属性数据2”的模样式.这样就和Fill方法相对应了.该部分详细源码可见BaseEntity.cs文件.
EntityTypeCache则是一个实体类型缓存.
然后来实现一个关键的ModelBinder.也就是本Demo中的EntityModelBinder,这个类实现了IModelBinder接口(也可以继承字DefaultModelBinder,不过在这儿直接实现IModelBinder来做示例).系统将url中的字符串转换成实体就靠他了.先贴代码:
该方法中.我们首先检查是否包含有名为modelName的参数,如果有,则直接调用Fill方法.用该参数对应的值来填充对象.如果不存在,则检查Form是否为空,如果不为空,则用Form来填充对象.如果Form也为空,则检查在QueryString是否为空,如果不为空则用QueryString来填充对象,如果都为空,则返回null.这儿仅为一个例子,在实际项目中,可能这儿需要其他方式的控制和判断.甚至创建多个ModelBinder来分别绑定不同的实体
接下来我们创建一个实体对象Article,该对象继承自BaseEntity:
这是一个典型的由简单类型构成的实体对象.然后我们写出期望的Action,就如上一篇开始所示:
这样难道就可以工作了吗?当然不行.虽然我们建立了一系列的机制使得实体和url字符串之间可以转换了,但是系统并不知道我们拥有了这样一套机制,他仍然按照他的老一套来处理,自然无法得到正确结果.因此,我们需要将实体和ModelBinder对象的关系告诉系统.如何来做这件事情呢?我们这时候来回顾下上一篇中的代码段:
这儿演示了两种情况,是不是意味着我们有两种方法来注册绑定关系呢?答案是肯定的.对于一般的类型,是没有定义CustomModelBinderAttribute的.因此在ModelBinders中注册绑定是可以的.同时,也可以给类型定义个CustomModelBinderAttribute.这样会执行case1中的代码了.要实现第一种方式,只需要在系统执行之前注册绑定,可以在Global.asax进行,代码如下:
这样系统在执行Action的时候就能根据自定义的ModelBinder来转换数据了,但是,这样做也有一个问题,如果系统有很多的实体类,一个个注册岂不是麻烦死.这时候就该CustomModelBinderAttribute出场了.我们更改下Article的定义,也可以达到同样的效果:
在这儿直接为该类指定绑定的Binder类就可.
下面来看看在view中如何使用:
然后用先前的Add方法获取这个Article.3个连接的结果相同,如下图:
完全达到我们事先的要求.
下面我们再测试Post的情况.新建一个AddForm的Action,在View中代码如下:
我们来执行:
按下提交,效果如图:
也能达到我们的要求.再次感叹下ModelBinder的优雅
本文作者: