ASP.NET开源框架之HIPPO技术内幕(二)--Meta-Data驱动
二、Meta-Data驱动
上一章从大的方向上介绍了一下HIPPO系统,从本章开始进入正题。
为了更好的组织系统的结构,把所有的基础信息抽取出来存储在数据库中。这种方式从BroadVision中学习得来。
为了实现这种数据的存储,需要几种不同类型的对象来存储,如下:
对象 |
表名 |
含义 |
Schema |
ZR_SCHEMA |
模式 |
Content |
ZR_CONTENT |
内容 |
Table |
ZR_TABLES |
模式对应的表 |
Attribute |
ZR_ATTRIBUTE |
每个表的所有的字段描述 |
在上述表达中,Schema是整个Meta-Data结构的最底层,所有的信息都要依附于它,那么到底什么是Schema呢,直译就是“模式”,系统中所有的内容描述最后都要归结为Schema的表达,如“新闻”、“用户”、“产品”等一系列内容,也就是可以理解为同类的内容属于一个Schema。
Content就是内容的意思,根据CMS来看,它应该是比较重要的一个层面,没错,在本系统中,Content实际上是对Schema的扩展,他拥有更多的属性,并且Schema和Content必须是一一对应的,如果有可能,可以理解为这两个属于同一部分,在实际应用中,我也做了一些触发器,当修改Content的时候,会同时去修改相关联的Schema,那么一定有人会问,那为什么还要分成两部分来存储呢,呵,说来话长,本来想完全学习BroadVision的方式,但是最后做了简化,所以有了今天的样子。
Table理解起来就更容易了,它完全没有上面两个对象的抽象与神秘,就是用来说明上面的内容都是用哪些表来存储数据,本来嘛,在关系型数据库中,说来说去,不管表现层如何,最后必须把数据存储在具体的表中。Shema与Table是一对多的关系,即同一个内容可以用多个表来存储,举个例子,“用户”这个模式,分成几个主要的表,一个是ZR_USER,里面存储着会员ID,会员登录号及密码等几个最基本的字段,ZR_USER_PROFILE这个表,存储着用户的扩展属性,如会员姓名,生日,身份证等。用的时候,把两个表加起来可以相当于一张表来使用。这种数据存储方式与把用户的所有字段均写在一个表中是两种完全不同的设计思想,其实本质上并没有谁好谁坏的区别,最主要的区别是在数据量很大的时候,我这种设计方式对数据库的压力会小一些,因为在登录的时候,只需要检查几个有限的字段,即便进行全表描述,也会占用更小的IO操作。还有第三种表,就是“子表”,我来给子表举个例子,一个会员可以拥有多个Email地址,如果把这些字段全放在一张表中,那就要预留多个字段,所以把它单独建一张表,然后用一个外键和用户表对应起来,就可以表达用户与EMAIL的一对多的关系,在前端展示的时候,会为这种一对多的子表有一个专门的表现方式。
Attribute更容易理解了,其实就是数据库中的字段,也就是说上面的Table所对应的字段的描述都存放在这个表中。为什么不叫Column呢,因为叫Attribute更具有对象的感觉,感觉能超越数据库似的,哈,其实没有区别。
其实在任何的数据库中,都可以找到一个系统表,里面存储着每个TABLE所对应的字段描述,为什么我们还要专门建立一个自己的表来重复存储呢?很显示,系统存储的信息完全是DB自有的信息,不能扩展,而且每个数据库的存储方式也不同,所以把它单独提取出来。
在系统中,开发了四个类来实现对以上四个表的描述,另外,还专门建了两个管理器的类对上述几个类进行管理,如下:
SchemaManager
ContentManager
使用这两个管理器可以创建新的Schema或修改已经存在的Content等。总之,这一切工作的想法就是尽量减少使用者与数据库的直接打交道,把所有的操作全部封装起来。用起来更有面向对象的感觉。
这里面有一个问题值得谈一下,使用对象方式的时候,对象的属性是在写程序的时候固定的,比如会员的身高、体重等,这些固化的属性并不会随着定制化而改变,可是数据库中的表完全有可能在定制过程中变得千差万别,那么用对象描述的时候怎么样保证他们能顺利的对应起来呢,我的做法是找几个固定的字段做成类的属性,如登录名,密码等,而姓名,身份证等信息,在User类中并找不到一个真正对应的属性,而是把所有这些属性放在一个大的Hashtable中,形成一个复合的属性,取个名字叫做 Properties,所以在使用的时候可以按如下方式引用
user.UserAlias 是用户的登录名
user.Properties[“NAME”] 就是会员的姓名
由上可以看出,不管对属性做了多少扩展,都可以用这种方式来很好的呈现了。
关于用户的密码存储及使用,后面会有专门的章节来讨论。
再来举个例子,如何取出所有的新闻:
假如新闻的Content的名字是EDITORIAL,则程序可以这样写:
ContentManager contentManager = new ContentManager(“EDITORIAL”);
DataTable table = contentManager.GetContent();
只要上述两句简单的描述就可以把所有的新闻取出来放入一个DataTable中备用,后期可以利用它直接填入DataGrid中。
在很多情况下,并不需要取出所有的新闻,这时就要做一个条件过滤,或取出前10条,或按某个字段排序等,针对所有这些功能,都已经对contentManager.GetContent()函数进行了多种重载,只要加上适当的参数即可,由于篇幅有限,不能一一说明。
听到现在,可能一直都是比较抽象的,也不知我到底要做什么,到底在干什么,但是Meta-Data这部分是系统的核心思想,理解这部分内容,即便不看后面的章节,也会对您以后的设计有很大的帮助。从下一部分开始,会进行部分关键代码的描述。
李鸣(aicken)原创 转载注明