SPLEB开发日志——初篇
开始写SPLEB已经五天了,才开始写开发日志似乎有点晚了,其实不是。开始做SPLEB的时候我并没有把握,说实话,我作为一个初学者,没做过什么正儿八经的东西,SPLEB写到现在了,有了很多心得,也有了一些把握可以做完了,所以辟了这么一块地儿。
我做过的网站不少,但都是只做不维护,希望那里能成为我认真维护的第一个网站,但是精力有限,事情太多,我尽力而为。
SPLEB-System Design Map
下载清晰版
SPLEB开发日志——相关资料
1, SPL V3、SPL Robot:持久层
Ref:http://tintown.cnblogs.com/
SPL全称Smart Persistence Layer,SPLEB全称:Smart Persistence Layer Entity Builder
SPLEB即为SPL的实体类的生成器,SPL是一个轻量级的、灵活、强大的数据持久层,而SPLEB的作用,就是将SPL的功能发挥出来。
2, CodeSmithV3.1:代码生成器
SPLEB采用CodeSmith的API作为代码实体类生成工具。以后的开发中,大家将看到采用Template的模板类,提供无限的可扩展性。
SPLEB开发日志——CodeSmith原理剖析(1)
本文介绍了在SPLEB(http://yuandong.cnblogs.com/archive/2006/02/01/324647.html)开发过程中,对CodeSmith中API调用的一些研究,分析代码生成的技术。最终可以在我们自己的程序中调用CodeSmith 的API作为代码生成的基础。本文假设读者已经可以熟练的使用CodeSmith编写模板并且批量生成代码,且了解代码的动态编译。
一, CodeSmtih的代码生成原理
CodeSmith采用的类似ASP.net的模板,运行时将模板通过自带的编译器编译为标准的.net类,然后调用该类的Remder方法进行模板的输出。具体流程如图:
CodeSmith编译的模板生成的类以CodeSmith.Engine.CodeTemplate为基类,提供Render方法用于输出代码文件。
在编译时,CodeSmith调用CodeSmith.Engine.CodeTemplateCompiler类对模板文件进行处理,转化为标准的.net代码文件,然后再调用.Net的编译器编译为.net程序。
CodeTemplateCompiler通常根据一个.cst文件进行构造,然后调用Compiler()方法进行编译。如果编译成功(根据Error.Count==0判定),则调用GetInstance()方法得到CodeTemplate类的对象。
CodeTemplate是所有模板编译后生成的类的基类,该类提供SetProperty()方法设置模板的属性,提供Render()方法将模板内容根据模板的属性,将模板的内容输出为文本、或者输出到文件。
CodeTemplateCompiler编译模板的原理其实也不复杂,将模板头语句进行解析和转化;将不变的部分用WriteLine进行输出(例如你写了一个空行,则转化为WriteLine(););将代码块(<Script runat=Template> </Script>)直接复制;将<%%>稍作转换。当然,实际的实现不想描述的简单。
实际生成的代码可以通过CodeSmith Studio中Tools-CompileToAssembly生成为DLL。
二, CodeTemplate与GUI
.Net环境提供了一个控件:PropertyGrid,用来对类的属性进行设置,该控件最重要的属性是SelectObject,只要将需要进行设置的类指派到该属性,控件就可以利用反射发现该类的属性和设计时标签(Attribute),进行显示和配置。
CodeTemplate类经过CodeTemplateCompiler类编译得到的对象,可以直接绑定到PropertyGrid进行设置,CodeSmithStudio即是采用了这种方法。但是CodeSmith在CodeSmith Explorer中采用了一个自己开发的,继承于PropertyGrid的EnhancedPropertyGrid进行主要的设置和输出工作。
CodeTemplate类继承于CodeSmith.Engine.Control类。并且将所有不用于客户端设置的属性加上Browsable(false)标签,不在PropertyGrid中显示。
在使用CodeSmith API的使用,利用这一点和GUI进行结合,效果非常好。(人家本来就是这么设计的嘛!)
SPLEB开发日志——CodeSmith原理剖析(2)
三, SchemaExplorer,数据库访问
CodeSmith 之所以如此强大的原因,即提供了方便的数据库访问和调用。此部分其实可以算是一个插件,即SchemaExplorer。
SchemaExplorer封装了所有数据库对象。提供了DatabaseSchema、TableSchema、ColumnSchema等的数据库Schema,并且采用动态加载Assembly的方法提供了对数据源信息的提取,该功能由IDbSchemaProvider接口实现。在CodeSmith的网站上提供了许多组件。V3.1提供了ADOXSchemaProvider和SqlSchemaProvider,用于对ADO和Sql数据库的访问,类似ADO.NET中的OleDbDataAdpater和SqlDataAdpater。
DatabaseSchema代表一个数据库。根据ConnnectionString和IDbSchemaProvider进行构造。其主要的属性是TableSchmeaCollection和ViewSchemaCollection,提供了该数据库的数据表和数据视图信息。
TableSchema代表一个数据库表。主要是提供了ColumnSchemaCollection。即数据表的列数据。
ColumnSchema代表一个数据表的列。主要属性是ColumnName,SystemType,ColumnType,IsPrimaryKeyMember等内容
以上三各类都提供了ExtendedPropertyCollection,储存扩展信息,该集合类似HashTable,另外,很多数据库Provider提供的该类性的数据库的特殊信息也储存此处。
在模板中,可以通过迭代以上三各类的集合类,对数据库的存储过程,ORM等进行全自动生成。
SPLEB数据库设计
想和大家一起讨论一下如何做数据库的设计,更确切的说,不是讨论一些数据库设计的原则,而是在工程上,数据库的设计是什么样子的。我是初学者,没有参加过规范的项目,做过的都是自己一个人单挑的微型项目,摸索了一阵时间。以下是我作SPLEB的一份数据库设计,请大家批评指教。
我的数据库设计方式
一、数据实体:罗列所有的数据实体
二、数据明晰:数据实体应该包含哪些数据
三、数据实体关系图:数据实体之间的关系
四、数据表格:数据库Schema表格
样例如下(数据库较小,数据实体关系极少,未做关系图)
SPLEB数据库设计
一、数据实体
1, 系统设置(Config)
2, 数据源、数据库(Database)
3, 模板(Template)
4, 模板信息(TemplateInf)
二、数据实体明细
1, 系统设置(Config)
(1) 业务数据:命名空间(NameSpace)
(2) 逻辑数据:主键(ConfigID)
2, 数据源、数据库(Database)
(1) 业务数据:数据库名称(DatabaseName)、连接字符串(ConnectionString)、数据库类型(DatabaseType)、映射信息文件名(ClassMapFileName)、上次使用时间(LastTime)
(2) 逻辑数据:主键(DatabaseID)
3, 模板(Template)
(1) 业务数据:模版名称(TemplateName)、生成代码语言(Language)、主模版文件名(MainFileName)、描述(Description)、模板使用次数(BuildTimes)、上次使用时间(LastTime)
(2) 逻辑数据:主键(TemplateID)
(3) 关联数据实体:模板信息
4, 模板信息(TemplateInf)
(1) 业务数据:模板内容(Content)、模板文件名(FileName)
(2) 逻辑数据:主键(TemplateInfID)
(3) 关系数据:对应模板主键(TemplateID)
(4) 关联数据实体:模板
三、数据表格
1, 系统设置(Config)
数据名称 |
字段名称 |
数据类型 |
备注 |
主键 |
ConfigID |
主键 |
|
命名空间 |
NameSpace |
文本 |
|
2, 数据源、数据库(Database)
数据名称 |
字段名称 |
数据类型 |
备注 |
主键 |
DatabaseID |
主键 |
|
数据库名称 |
DatabaseName |
文本 |
非空 |
连接字符串 |
ConnectionString |
长文本 |
|
数据库类型 |
DatabaseType |
文本 |
非空,可能的取值: MsSqlServer、MsAccess、 Oracle |
映射信息文件名 |
ClassMapFileName |
文本 |
非空 |
上次使用时间 |
LastTime |
时间 |
|
3, 模板(Template)
数据名称 |
字段名称 |
数据类型 |
备注 |
主键 |
TemplateID |
主键 |
|
模板名称 |
TemplateName |
文本 |
非空 |
生成代码语言 |
Language |
文本 |
非空,可能的取值 C#、VB |
主模板文件名 |
MainFileName |
文本 |
非空 |
描述 |
Description |
长文本 |
|
模板使用次数 |
BuildTimes |
整数 |
非空,默认0 |
上次使用时间 |
LastTime |
日期 |
|
4, 模板信息(TemplateInf)
数据名称 |
字段名称 |
数据类型 |
备注 |
主键 |
TemplateInfID |
主键 |
|
对应模版 |
TmplateID |
外键 |
非空 |
模板内容 |
Content |
长文本 |
|
文件名 |
FileName |
文本 |
非空 |
SPLEB开发日志——三层结构(1)原理
借着SPLEB开发的机会,我也说一下我常用的系统的三层式结构,希望和大家分享,更希望大家批评指教。
系统结构如图(以Web为例):
1, 数据层:没什么好说的,数据库的选用应该和业务逻辑没有关系,总的来说,只是把数据库当作一个容器。数据库的特性一概不用(或许用人说这对性能不好,但是总比你迁移的成本要小得多,更何况你也不想M$或者IBM捏着你的命根吧?)。
2, DLL中间层:这个是重点了。
a) 数据访问(DataAccess):提供数据库的访问功能,我一般使用现成的,以前自己写了个YDClassLibrary,现在是用SPL。至于用什么好,还要看项目的规模。还要看项目的大小,像SPLEB这样的小东西(Access库,不到5个表,没有并发……)用NHibernate有必要吗?
b) 数据定义(DataDefine):数据定义也可以成为数据实体(DataEntity),这个部分就给出了基于DataAccess层ORM的OO类。不过换一套DataAccess的时候,就要换一套DataDefine了。一般这一层使用代码生成器生成,也和具体的业务逻辑关系不大。至于代码生成的工具就要根据你的DataAccess来定了。以前用YDCLassLibrary的时候用YDCodeMaker,现在用SPL所以在写个SPLEB。或者,更直接一些,用CodeSmith。呵呵。
c) 系统支持(SystemFramework):提供系统的框架支持,典型的,如异常、日志等。也有现成的,比如log4net。
d) 业务逻辑(BusinessRule):对于需求中逻辑的抽象,一帮是提供某些功能。比如一个用户的信息转换、加密等。
e) 业务外观(BusinessFacade):对业务层的封装,提供业务实体(BusinessEntity),及相关的业务的Façade类。对上一层提供接口。也可以将BusindessEntity单独抽出作为一部分。这要看具体情况。如果是大型的,则Business完全封装DataDefine部分,即对外只需了解BusinessFacade就可以了。
3, 表现层(WebForm or WinForm)没什么好说的了,看你怎么写了,Web或者WinForm,随意了,什么AJAX呀,WebServices呀,都可以。
说明:
1, 我的三层式结构和微软给出的例子差不多,也有不同之处,主要是着眼于实际的快速开发。兼顾稳定性和性能。最大限度的利用已有的资源,特别是免费和开源的,重用就是减少成本!
2, 对于表现层,需要了解业务层的BusinessFacade类。对于小系统,有的时候DataEntity和BusinessEntity也差不多,所以表现层也可以直接使用DataEntity。我认为封装是一个相对的问题,仅仅为了封装而封装没有任何意义,先实现功能,我们有TDD,所以我们可以不断重构,当然,设计模式也很重要,但我们的目的是实现最后的系统,时刻记住,我们是在作产品,不是艺术品。
3, 开发模式。首先当然是设计,业务对象、业务逻辑什么的,然后再设计到一定阶段的时候,我推荐先做原型或者先把所有的UI(Web是页面设计,WinForm是窗体)做出来,和客户交流,这样比较能把握需求,设计的也更准确,可以极大的避免返工。然后差不多开始数据库设计,可以参考我的另一篇文章(http://yuandong.cnblogs.com/archive/2006/02/04/325303.html)。只要数据库设计完毕,DataAccess和DataDefine就全部是自动化的了。
4, 业务逻辑,这是作最难写的,我一般采用OOA/D和TDD相结合的模式,循环进行,这个过程中一般少不了对数据库的更改,幸好我们的数据访问全部是自动的,很容易适应需求变化。
5, 进入表现层了,这时候业务逻辑应该已经全部通过单元测试了,我们有了足够的类来搭建我们的外观了。如果你前面已经作好了界面设计,那就太容易了,装备,调试,一切OK。
具体的实现,我会用SPLEB为例,请关注我的下一篇文章
ReTrunBack http://yuandong.cnblogs.com/