在进行cs细节分析的之前,有必要先了解cs工程(解决方案)的组成,以及组成cs工程中项目的结构,本文分为三个部分:1、工程结构 2、三层构架 3、数据库构架。
1:工程结构
cs工程主要分为4个部分
a:系统底层构架项目communityservercomponents、communityservercontrols,提供给其他项目父类、接口、全局变量、cs系统设置、公用用户自定义控件、用户与权限管理业务逻辑、异常处理等。
b:communityserverblogs、communityserverforums、communityservergalleries、communityserverdocuments、communityserverguestbook。这些项目都是通过继承、调用全局方法等实现自己的业务逻辑并且抽象出自己的data provider,业务逻辑不同,但项目都是采用三层结构。
c:ui项目,这里指communityserverweb。该项目中几乎不包含逻辑代码,只是简单的html与对运用项目中的skin(skin是 *.ascx文件,但没有关联相应的*.cs,大致可以这样理解:如communityserverblogs 中的skin 文件*.ascx相关联的*.cs逻辑代码在communityserverblogs项目中实现,并且不保存在与*.ascx文件相同的目录下,而是与communityserverblogs中其他业务逻辑一起编译为communityserver.blogs.dll)。同时ui项目中还保存了languages文件与一些配置文件等。
d:dataprovider,目前只实现了sqldataprovider,对sql server数据库进行操作。dataprovider实际上是对b部分中实体项目数据库操作抽象的具体实现。数据操作的provider方式带来几个好处,不关心具体实现、支持多数据库、有利于团队协作分工等。
2、三层构架
在communityserverblogs、communityserverforums、communityservergalleries等几个项目中都采用了三层构架,如下图:
业务逻辑是穿插在contorls与components中,在单独的一个项目中contorls与components体现在namespace里,下面以communityservergalleries项目为例讲述一下具体项目的三层结构:
为了便于文件的管理,项目中建立了components与contorls文件夹分别存放名字空间为communityserver.galleries.components与communityserver.galleries.controls。如果你是一个初学者或者对三层结构不是太了解,可能很多时候你会对三层构架感到困惑,其实这个层的概念没有绝对的划分界限,更不是用类作为最小的单位。这种划分是相对的,是一种为编写代码功能的划分。communityservergalleries项目中没有直接编写对数据库的操作代码,而是使用了provider的方式把操作的方法进行抽象:
例:public abstract hashtable getgalleries(bool mergepermissions);
抽象后的代码可以和普通方法一样被业务逻辑调用,由于使用了provider的方式,使得数据层与业务层之间是松散耦合的,可以很容易的进行数据库更换(只需要更换对抽象数据操作类的具体实现方法,而不会影响到业务逻辑层的代码)。
业务逻辑包括几个部分:communityserver.galleries.components下所有的实体类,这些实体类大多数通过继承post、ithread、permissionbase等在communityservercomponents项目中定义过或者申明过的类与接口。communityserver.galleries命名空间下的一些类,这些类用来处理业务逻辑运行过程中的数据,同时进行缓存和过滤等操作(过滤操作是通过在communityservercomponents项目中的csapplication.cs类下定义委托与事件完成的,要理解这个过程需要对cs有一定的了解,后续我会做一个cs中委托与事件的专题)。还有一部分业务逻辑混淆在communityserver.galleries.controls命名空间下的一些类中,他们与ui表示层较为紧密,你很难准确的定义他们是属于业务逻辑还是表现层代码。
cs中表示层中的类大致可以分为三部分,1:是需要*.ascx的直接处理用户界面或者用户输入输出的代码,这些类都间接的继承communityservercontrols项目中的templatedwebcontrol类。2:要进行换肤就少不了使用一些辅助的类,这些类提供一些基础服务,如:找到*.cs文件对应*.ascx所在路径等。3:是不需要*.ascx的用户自定义控件,一般继承自.net提供的webcontrols。这些类被放入controls/utility文件夹下面。
传统的asp.net web页面设计时在建立*.aspx或者*.ascx都会同时建立一个同名的*.cs文件,用来实现对页面中控件的操作,页面这个时候就像一个容器。通过codebehind页面在运行时会自动找到对应的类(这个过程如何实现没有去分析过,但是我们可以通过反射达到同样的效果,同时可以获得更高的灵活性)。cs系统中的ui就是通过反射寻找到*.ascx对应的类从而实现相应的ui处理函数,而*.ascx只要保持名称和内容中控件的id不变,具体html代码如何更换并不影响到整个系统的功能,cs系统也正是通过这样的手段达到换肤的目的,同时加入masterpage又可以减少html代码中重复部分。最后html与css样式表的结合你就可以很容易改变网站的皮肤的样式,包括文字样子和div的布局了。*.cs与*.ascx文件剥离后网站美工与程序设计人员就真正的分开了,有利于团队协作,发挥个人特长。
还有一点必须说明:在cs项目中很多*.aspx文件只是一个加入了masterpage的框架页,甚至是一个什么都没有的空文件(如多数default.aspx页面),框架文件中嵌入了大量的类似于“<galleries:galleryadmin id="galleryadmin" runat="server" />”这样的控件,其实这个控件对应于skin-galleryadmin.ascx的皮肤。如果你能理解到这里,想看明白cs的大部分代码应该不会有问题。
3、数据库构架
先看一下dataprovider模型:
模型中可以看出抽象的dataprovider是与具体的数据库操作dataprovider分离的,在cs中components与抽象的dataprovider被编译在一个项目中,而sql server dataprovider则被单独的编译出来。好处都可以看到那就是更换不同的dataprovider抽象实现不同的数据库操作,另外这种松散耦合的方式有利于团队开放。如何实现这样的dataprovider方式呢(我这里简述一些,具体的请关注后续的专题)?
先看抽象类,抽象类被存放在相应项目的providers目录下,以gallery项目为例子,它的命名空间是communityserver.galleries.components
整个类都是public abstract class,这个很好理解,其实关键的是在“instance”
部分,在 instance里通过调用communityservercomponents项目dataproviders.cs类中的createinstance方法初始化一个gallerydataprovider。
过程是先在communityserver.config文件中找到
public static readonly string gallerydataprovidername = "gallerydataprovider";
中的“gallerydataprovider”,这里为:
<add name = "gallerydataprovider" type = "communityserver.data.gallerysqldataprovider, communityserver.sqldataprovider" connectionstringname = "sitesqlserver" databaseownerstringname = "sitesqlserverowner" />
根据“type”中的内容,运用type.gettype与activator.createinstance把communityserver.sqldataprovider.dll程序集中对应的communityserver.data.gallerysqldataprovider类实例化,实例化后类似gallerydataprovider.instance().getgalleries(true)的调用其实就是直接操作communityserver.sqldataprovider.dll程序集中communityserver.data.gallerysqldataprovider类下的
public override hashtable getgalleries(bool mergepermissions)方法。这个过程可能比较难理解,但是理解只是时间问题。
数据访问层的中与数据库最紧密接触的就是sqldataprovider(sqldataprovider是对sql server数据库操作抽象的实现,你也可以对其他数据库进行抽象实现,目前cs只提供sql server实现),在sqldataprovider里使用的是对存储过程的操作而没有使用sql text。在前一片专题中我写过这个做的好处,这里不再多说。主要说明的是对数据的缓存与序化:
缓存:我个人习惯是把缓存写在数据层里,而cs是把缓存管理写在业务逻辑层中,而且缓存的数量是很大的,如对communityservergalleries项目中的读取单个gallery方法:
public static gallery getgallery(string applicationkey, bool cacheable)
一般的做法是为这个方法写一个存储过程,然后当有数据操作的时候从数据库中调用相关数据,同时根据参数是否缓存数据,这看起来很好。我也总是觉得内存宝贵,能少缓存一点就少缓存一点,但是cs的做法是把全部gallery读入hashtable,缓存掉!要读取单个gallery的时候从缓存中找,根本不去管数据库,更不要写存储过程(这倒是很方便)。当然了,cs中是对缓存定义了时间的。时间到期后缓存就自动被释放了,但是在缓存释放之前新的数据是不会被显示出来的,对于一些更新不是很快的数据集来说这算是一种比较好的解决方案(在sql 2005中有更好的缓存解决方法,可以在新数据更新时更新缓存)。
数据序化:开发过crm的朋友应该都有体会,很多字段需要预留在数据库中,因为你不了解使用crm系统的客户会有一些什么样的存储要求,如:crm用户需要保存他客户的年龄,但是crm系统设计过程中不可能为这样一个问题特意的加入这个存储字段,通常的做法是给一些空字段,用户使用的时候相应的对他进行初始化。但是导致的结果就是crm的数据库惨不忍睹。更可怕的是,如果要进行软
先分析一下存储的数据,首先是propertynames字段,“enablecomments:s:0:4:moderatecomments:s:4:5:enableratings:s:9:4:” “enablecomments”其实是在实体类中定义的一个属性名称,“:”表示定义完毕,“s:0:
在cs的最底层就是数据库和存储过程了,关键的几个表cs_groups、cs_sections、cs_threads和cs_posts表分别对应于communtyserver.components命名空间下的group、section、thread和post类,groups是分组,对应论坛来说就是“板块组”、版块”、“线索”和“帖子”,对应于blog就是“博客分组”、“博客”“线索”、“随笔或者文章”。这个cs_threads有点难理解,其实它是对相应section下的post相关信息的统计与跟踪,如最后回帖时间、评论总数等等。
上面大致分析了community server项目的体系结构,细节部分会在后续的专题中一一的分析。不管怎么说community server是一个相对庞大的工程,要完全的讲解与系统的分析还需要很多的文字。
新闻热点
疑难解答