利用IExtenderProvider简化Entity和UI的数据交换(续)
前文提到用IExtenderProvider简化Entity和UI的数据交换,这里再补充一下。
看到1楼“非空”老兄的回复,看了CodeProject的链接。确实和我的类似,不过他还加上了一个控件对应的是哪个对象。这确实是比较有用的地方,因为我们的表单上的数据可能是由多个对象组合起来的。其实我本意也如此,为什么没有加入呢?当时考虑是很多时候会用动态代理来实现ORM,代理后的对象类型就和控件上设置的不一样了,所以抛弃了这个功能。昨天灵机一动,想到可以利用Type.BaseType来实现,于是又添加了一个ObjectSource属性。在UpdateUI/UpdateData开始时用while向上查找,直到BaseType为null。如果中途找到了,自然break,没有找到则不需要执行Update。
对于DataRow则取DataRow.DataTable.TableName,这时是不区分大小写的,毕竟数据库表一般来说我们也不会用大小写来区分表名吧。如果使用的是Object则使用Object的Type.Name,这时就需要用大小写区分,毕竟写代码什么情况都有。最后一种情况,不可能一个表单上有2个对象Type.Name一摸一样吧;如果是同一个class那我只能说抱歉,你改绑定吧;不是同一个class那这个太暴力了,我更是只能说抱歉了。(相信这种情况更微乎其微)
对于Web开发,反射和事件仍然可以使用,但是有2个问题:1是原先从Component继承的UIMapper无法出现在Web项目的ToolBox中。2是原先存储控件映射信息的是个私有属性,且也是有Dictionary实现的;WinForm中数据存储在资源中,WebForm时数据只能放在页面上。
这里给出一个最容易实现的方法。首先,做一个Wrapper(名字就叫WebUIMapper),目的是使UIMapper作为一个Web服务器控件可以拖放到页面上。同样做一个属性去存储映射关系,为了方便编辑,可以实现一个自己的属性编辑器(就像CodeProject上的那个demo)。由于Web页面查找控件的方法比较特殊一些,所以还必须有一个由使用者自己来决定如何根据ID查找控件的方法。我的习惯是是用event来实现,名字就是QueryControlEvent,事件的参数QueryControlEventArgs包含了控件的ID,默认方法查找到的控件(可能为null,因为没找到),使用者可以根据ID自己实现查找的方法,并给QueryControlEventArgs的Control属性赋值,以实现自定义查找。而其他的赋值和类型转换则完全由UIMapper来搞定了。
其实,也可以实现像微软的数据源选择那样的功能,CodeProject那个Demo那样通过硬编码写入的数据源(demo嘛),或者通过配置文件设置需要做映射的Model所在的Assembly。但目前觉得这样做并不会带来太大的方便,不如就使用直接输入的方式吧。同样,WebForm和WinForm上需要进行映射的控件也采用直接输入的方式。虽然可能容易出错,也无法提供编译时的检查;但定义的时候简单,维护也比较方便,最关键的是可以做到用一种统一的方式来处理UI和实体间数据交换的问题。
最后,再提一点,在Web上使用IExtenderProvider就显得不实际了,所以那个作为Wrapper的WebUIMapper直接从WebControl继承就可以了。映射信息集的定义,则通过WebUIMapper的属性编辑器来完成。
