Kanas.net 快速入门
广州的九月已经入秋,天气已经不再象前段时间那样酷热。在这个季节去看看喀纳斯的美丽景色是一年中最佳的选择。如果你没有去美丽喀纳斯的时间和经费上的预算,那就请随我进入Kanas.net之旅,虽然不如去喀纳斯那么浪漫,但也是一个退而求其次的选择。
Kanas.net Framework是一个非常易于使用的持久层框架,如果你有非常好的面向对象的基础,使用这个框架来实现对数据的透明访问,应该会让你感到满意。如果你质疑我的这个结论,那么你先随我启动这个很小的示例,我相信你会和我有相同的结论。
第一步:下载Kanas.net Framework的最新版,并且安装到系统中。
Kanas.net Framework是一个很小的框架,安装到系统中的内容包括核心源代码和全部的文档也不会超过
这里需要做一个小的说明。你从博客园下载的安装包中仅包括核心源代码及部分测试代码、MSDN风格的帮助文档、VS.NET插件三个部分。对于希望了解Kanas.Net框架的用户来说已经足够了。更完整的安装包包括一个可视化工具(可以从各种数据库模型、Visio数据库设计模型和PowerDesigner数据库设计模型中直接导入到Kanas.Net的实体关系模型中)、一组PowerPoint/Word教程和一系列扩展包,由于涉及版权License问题目前尚不开放,仅在本公司范围内发布。
第二步:如果你的系统中安装有VS.Net,可以直接打开安装后的快捷方式Kanas.Net.SDK解决方案,然后你就可以看到解决方案中所包括的五个项目:
- Kanas.Utils:实用程序项目。关于这个项目的详情我曾经写过一篇文章来介绍。
- Kanas.Common:基础架构项目。这个项目与我公司内部发行的版本略有差异。后者加入了三个重要的基础架构:基于XML的DOM架构、基于XML的配置架构和数据集的自由导出架构。
- Kanas.Framework:持久层项目。包含所有的持久化服务。
- Kanas.Contacts:联系人项目。这个项目选自Microsoft Office的Access示例数据库Contact。虽然该数据库非常简单,所包含的关系非常少,但作为一个入门的选题可以避免很多复杂的问题。在这个项目中可以看到所有基于Kanas.net Framework的项目如何构造一个配置环境、如何通过实体关系模型来产生相关的业务代码并令你在业务模块中轻松地解决复杂的业务问题。
- Kanas.Test:测试项目。这个项目是一个单元测试项目,如果你的系统中安装有TestDriven.net,你甚至可以在NUnit的GUI中直接Run起来,相信你能够看到全部的绿灯。
第三步:现在先不要急于研究前面的三个项目,将注意力放到Kanas.Contacts项目上。这是整个解决方案中最重要的项目。该项目演示了Kanas.Net的几个非常有用的配置或扩展功能:
- 通过实体关系模型生成实体代码
- 通过配置来初始化业务环境
- 自定义数据库提供器
- 自定义约束子
- 自定义约束子的数据库实现
首先让我们先看看ER模型。在Kanas.Contacts项目中有一个Contact.erm文件,其生成操作是“无”,但定义了自定义工具“KanasCodeGen”。请以“HTML/XML编辑器”方式打开这个文档,你立即可以看到整个实体关系模型,除了数据库模型中的表格及字段外,还包括一些额外的内容,如领域概念模型。
ER模型的根元素是“ERModel”,其中规定了一些主要的属性:
- description:模型的描述。
- namespace:生成代码的命名空间。
- codelang:生成代码所用的语言。CSharp就是C#,VB就是VB.Net。
- workingpath:工作路径,即生成代码文件的路径。该属性不控制代码生成插件。VS.NET插件总是将代码生成到erm文档所在的目录下。
- layout:生成代码布局,可选的项是Single(生成单个代码文件)、Chunk(每个类型单独生成一个代码文件)和Build(不生成代码文件,直接编译为程序集)。该属性不控制代码生成插件。VS.Net插件总是将代码生成为单个文件。
Contact项目不包括枚举。所以在enums下未包含任何元素。在我的另外一个项目PMC中,由于“缺陷管理”中的选项极多,居然多达十多项。
entities节中包括所有的实体。每个实体元素以entityid属性开始的一系列属性,name(类型名称)、description(类型说明)、aggregated(是否需要构建集合类型)、mapping(映射到数据库中的表名)等。每个entity节中包含若干个property元素,每个property元素则包括name(属性名称)、type(属性类型)、description(属性描述)、mapping(映射到数据库中的字段名称)等属性。
点击“解决方案资源管理器”的工具栏中的“显示所有文件”按钮,可以看到contact.erm文档下有一个contact.cs文档。打开这个文档可以看到所有实体的代码。
现在我们试试修改一下erm文档,看看生成的源码能否自动同步。
1.打开Contact.cs单元,找到Contact class,可以看到一个Calls属性,说明为“获取对电话的聚合”:
/// 获取对电话的聚合
/// </summary>
[MappedCollection(typeof(CallCollection), typeof(Call), 1)]
public CallCollection Calls
{
get
{
return ((CallCollection)(this.EnsuredCell.GetCollection("Calls")));
}
}
这个属性是一个典型的一对多的聚合关系,通过该属性收集该联系人的所有电话记录。这个属性生成的原因是在Contact.erm文档中Call实体的Contact属性后面的Aggregated Attribute。试着删除这个属性,并保存erm文档。再回到contact.cs页,你会发现Contact.Calls属性没有了。我们再一次修改erm文档,在Call实体的Contact属性元素后面添加一个属性:
并保存erm文档。再回到contact.cs页,你会发现Contact.Calls属性又回来了。
erm文档是整个业务系统中的关键文档。利用这个文档可以实现部署上的一系列功能,例如重建数据库。
接下来我们看看如何初始化业务环境。根据Kanas.net教程,使用Kanas.net提供的Adapters全局对象或AppContext来初始化业务环境:
- 通过注册数据适配器或数据提供器来建立数据库引擎;
- 通过绑定业务类型到指定的数据适配器中来获得每个业务类型的访问方式。
- 通过安装业务捕获器来干扰数据加载及持久化。
- 通过安装可安装应用上下文部件来注册绑定到应用上下文的工作部件。
但是在Kanas.Contacts项目中没有使用上述的方式,而是采用配置来实现。
在Kanas.Contacts项目中最主要的单元就是Services.cs。这个单元中的Services类是整个业务模块的“引导类”,所有业务环境的初始化及持久化服务都可以通过该类型来实现。该类型的构造器是私有的,所以该类必须通过静态工厂方法来获得实例。该类型有一个静态方法和一个静态属性,前者通过指定配置文件所在的路径来初始化,后者通过静态属性获得单件(Singleton)实例。
分析Services类的构造器,可以发现,Services通过Kanas.Utils.ConfigLoader来通过配置文件初始化业务环境。打开配置文件Kanas.Contacts.Config,可以看出来用户能够自由地控制整个初始化过程。
需要说明一点:在配置文件中的adapter节,有一行配置logger的过程。平常在项目中一般可以采用类似
这样的配置来将数据库日志写入指定的文件中。但在本项目中通过自定义的日志记录器来实现,其目的一是可以在NUnit的GUI中立即看到数据库访问过程,二是可以演示如何自定义日志记录器或者说如何与其他日志框架连接。
虽然Kanas.Net Framework提供了OleDbProvider供访问Access数据库,但是Kanas.Contacts项目却采用了自定义的MdbProvider数据库提供器。其目的有两个:一是将原来OleDbProvider的构造器中的ADO.NET连接串参数改为Mdb文件名参数;二是演示如何自定义基于ADO.NET的数据库提供器。
Kanas.Contacts项目还自定义了一个约束子,这个约束子的功能是描述实体指定的日期属性的月日与给定的日期的月日相同。这个约束子的想法是基于一个非常合理的需要:查找生日在某个日期的所有联系人。按照Kanas.net的缺省方式,无法在数据库层面解决这个问题,只能放弃这个约束取出全部的数据,而在实例化环节读出对应属性与给定的日期进行比较以确定是否需要加载这个实例。因为任何一个约束子都必须实现GetDataSelected方法。显然这种方式会损失很大的性能,因为Access访问引擎提供了通过Month(BirthDate)和Day(BirthDate)来获取月日。于是,我从PropertiedConstraint派生了一个DayConstraint,并且实现了DayConstraint的CriteriaBuilder,在数据提供器的Init方法中加入这个CriteriaBuilder,最终让这个约束子通过数据库查询来实现而不是通过运行时的匹配。
第四步:运行单元测试。
在Kanas.Test项目中,有一个TestBase作为所有测试类的基类。建立这个基类的好处是共享统一的初始化过程。从这个TestBase类派生了四个测试类:
Basic:基本测试。该测试包含了三个测试用例:
- 插入联系人
- 删除联系人
- 修改联系人属性。
LoadingTest:加载测试。该测试包含七个测试用例:
- 基本加载
- 加载已婚的联系人
- 按字符串约束子来加载
- 按嵌套的约束子来加载
- 按反引用的日期类型约束子加载
- 懒惰加载
- 按生日加载
AggreatedTest:聚合测试。该测试包含一个测试用例:
- 按联系人类型将联系人分类统计人数的测试
DataTableBuildTest:数据表生成测试。
- 基本视图测试
- 带快速查询的视图测试
- 带嵌套类型快速查询的视图测试
OK,Come on,Good lucky!