XDesigner ORM 框架设计
袁永福 2011-01-20
最新版本源代码下载地址 https://files.cnblogs.com/xdesigner/XDesignerORM.zip .
前言
目前业界已经流行着很多ORM框架,但笔者还是要开发自己的ORM框架,在此不展开关于是否需要重复发明轮子的讨论,反正是要开发这个ORM框架,名为XDesignerORM,简称XORM。笔者的数据库开发经验不大多,但有着很多的图形软件开发经验,相信笔者的一些开发理念能融入到这个ORM框架中,形成一个独具特色的具有实际应用价值的框架程序。鉴于工作量大,而且避免闭门造车,笔者选择了开源模式。
设计理念
首先讲讲笔者的设计理念,ORM框架就是为了实现应用程序中的一个个实体对象和数据库记录之间的来回映射关系。其实实体对象和数据库记录是业务实体模型的两种表现形式,两者应该是平等的,但笔者先尝试下从应用程序的实体对象为中心的设计策略。这样笔者可以借鉴对象序列化的思想,可以将数据保存到数据库中视为对象的针对数据库的序列化过程。
.NET框架已经提供了三种序列化过程:1.二进制序列化;2.XML序列化;3.CodeDom序列化。最常用的就是XML序列化,而且WebService就是以XML序列化为底层基础的。
在XML序列化中,对象中的属性是和XML文档中的节点一一对应,.NET框架并提供一种通用的XML序列化操作类型System.Xml.XmlSerializer来完整的完成XML序列化和反序列化过程。绝大多数情况下,开发者无需考虑也无需编写代码来实现对象和XML文档之间的映射过程,而在一些必要的情况下为对象类型及其成员添加System.Xml.XmlElementAttribute等说明性质的特性。
既然XDesignerORM本质上是基于数据库的对象序列化过程。于是可以参考Xml序列化的原理,应当包含以下几个部分。
1. 数据库处理层。能尽量屏蔽各种特定的数据库的特性。比如MS SQL Server, Oracle , DB2使用的SQL语法的些许不同。初期为了简单,只支持MS SQL Server,不支持事务。
2. ORM引擎。框架的核心,能读取数据库记录并创建对象实例,也能根据对象实例来更新数据库。
3. ORM特性标记类型。类似XmlTypeAttribute等特性,用于知名对象类型和数据库之间的映射关系。在默认情况下,对象类型名称就是数据库表的名称,对象属性的名称就是数据库字段的名称。
比如在一个客户数据管理系统中,存在以下几种类型
CustomerClass,表示客户的基本信息。
OrderClass,表示订单的基本信息。
ProductClass,表示货物的基本信息。
在该系统中,存在多个客户,而一个客户有多个订单,一个订单包含多个货物。于是构成了客户、订单、货物的三层的树状结构。
而在本程序的演示数据库SkyDemo.mdb中有个数据表Customers就保存着很多客户的基本信息。于是一个CustomerClass类型的对象实例序列化就成为Customers数据表中的一条记录。
类似的可以建立其以下的映射关系
对象类型 |
数据表 |
CustomerClass |
Customers |
OrderClass |
Orders |
ProductClass |
Products |
在.NET中,实现对象属性和数据库字段的映射关系不算很复杂。但需要实现对象之间的关联结构与数据库记录之间的映射关系。也就是说需要在数据库中保存对象之间的一对多的关系,其实多对多的关系实际上可以看做双向的一对多的关系。
XORM应该可以处理对象之间的多对多的关系。比如,一个CustomerClass客户对象有多个订单记录。于是可以为CustomerClass类型添加一个名为Orders的属性,该属性返回客户名下的所有的OrderClass对象实例。XORM框架读取数据库创建一个CustomerClass对象实例时也应该读取数据库创建该客户名下的订单信息并设置CustomerClass对象的Orders属性。
这样做有一个性能问题,当应用程序仅仅读取客户的基本信息,而无需处理客户名下的订单信息,此时XORM框架还完整的读取该客户名下的订单信息,这浪费时间浪费内存。于是XORM框架应当支持延迟加载,只有当应用程序访问CustomerClass对象的Orders属性时才会再次读取数据并填充订单列表。
在此可以参考代码注入的技术来截获应用系统访问实体数据类型的需要延迟加载的属性。
比如当使用以下代码定义CustomerClass类型的Orders属性
Private OrderClass[] myOrders = null; Public virtual OrderClass[ ] Orders { Get { Return myOrders; } Set { myOrders = value; } } |
则采用动态编译技术从CustomerClass类型动态的创建一个类型,该类型重载了Orders属性,其代码如下
Public override OrderClass[] Orders { Get { If( base.Orders == null ) { // 读取Orders数据表并生成 OrderClass 对象实例数组。 Base.Orders = XORM.ReadObjects(“Orders” , typeof( OrderClass ) ); } Return base.Orders; } Set { Base.Orders = value; } } |
这样当应用程序调用XORM框架生成CustomerClass实例时,实际上获得的不是CustomerClass类型的实例,而是从CustomerClass上面派生的类型的实例,但使用上没有什么区别,应用程序仍然能将其当做CustomerClass类型来使用。
因此编写实体类型时,对于所有需要延迟加载的属性都需要设置为可以重载的模式,对于C#也就是添加virtual关键字,而且该类型是可以派生的,不能是密封类型,对于C#也就是不能在类型前面加上sealed 关键字。
延迟加载不但能用于处理子对象列表,也能用于处理包含大量的数据的字段,比如保存图片的二进制字段。
XORM框架还需要处理对象缓存的功能,尤其是在处理业务对象存在多对多的关系的时候,可能会出现对象重复加载的现象,此时需要重复使用已经加载的实体对象,目前暂不考虑。
使用XORM框架,实体对象与数据库的映射关系将使用特性附加在程序代码中,当数据库表或者字段发生变更,此时需要修改程序源代码,重新编译重新部署,这样降低了程序的灵活度。为此XORM框架将同时支持程序代码中的特性和XML配置文件的方式。XOMR框架首先分析程序集中的元数据,然后再分析XML配置文件,最后确定出实体对象和数据库的映射关系。
XORM框架能否快速方便的应用,需要配套使用方便的系统开发和实施辅助工具程序。这些工具程序包括:
1. 代码生成器。能根据数据库的表和字段结构自动生成能用于XORM框架的实体类型的源代码。也就是生成类似上面提到的CustomerClass,OrderClass等类型的源代码。代码生成器能根据数据库中定义的外键信息生成子对象列表属性。比如自动生成CustomerClass类型的Orders属性,并自动设置为延迟加载。
2. 实体类型和数据库的对比工具。能检查已经开发好的实体类型与指定数据库的映射关系,帮助开发者调试和部署XORM框架。
3. 映射配置文件编辑器。能帮助开发者准确的写出指定的数据库和实体类型之间的映射关系配置文件。
模块划分
XORM框架大体分为以下几个程序模块
ORM DOM
ORM DOM 就是一种描述O/R映射关系的文档对象模型。一个实用的O/R映射关系内部比较复杂,是难于用平面的数据结构来描述的,而文档对象模型擅长于描述复杂的数据结构,因此在此自定义了一种ORM DOM来描述O/R映射关系。
ORM DOM主要包含了以下几种类型
1. ORMProjectInfo类型,是DOM结构的顶级对象,是其他模块访问ORM DOM的唯一入口点。
2. ORMTypeInfo类型,用于描述一种实体类型和数据库表的映射关系。
3. ORMPropertyInfo类型,用于描述实体类型的某个属性和数据库中的某个字段的映射关系,还指明该字段是否延迟加载、是否是只读的、存储格式等等信息。
4. ORMRelationInfo类型,用于描述实体之间的关系,包括一对多的关系和多对多的关系。
ORM DOM 是可以参与XML序列化和反序列化的,因此这为未来同时支持O/R配置文件做好基础。
此外ORM DOM还定义了ORMTypeMappingHelper类型。该类型定义了执行数据库映射关系的接口,包括初始化查询、新增、修改和删除数据库记录的SQL语句。而其他模块将从整个类型派生出实际使用的帮助类型,而框架引擎在该类型的派生类型的帮助下实现O/R映射。
ORM DOM中已经实现了ReflectionORMTypeMappingHelper,该类型是基于反射技术的,运行速度比较慢。而框架将使用动态编译技术生成快速的映射帮助类型。
DataBase
这是本框架的数据库操作模块,包含了ORMDataBase和ORMDBCommand类型。
ORMDBCommand类型用于存储SQL命令文本以及配套参数值。
ORMDataBase类型用于管理数据库连接。
Dynamic Compile
这是本框架的动态编译功能模块,包括了CodeGenerator和DynamicCompiler类型。
CodeGenerator类型是代码生成器,是根据ORM DOM的信息,生成操作特定实体类型和特定数据表之间的映射关系的C#源代码文本。这段源代码会从ORMTypeMappingHelper类型派生出新的类型并实现其中的方法。
在生成的代码中,会根据需要从实体类型派生出临时类型,重载需要延迟加载的属性,这样实现了实体属性数据延迟加载的功能。
例如实体类型CustomerClass描述一个客户的基本信息,其定义如下
[ORMType("Customers")] public class CustomerClass { public CustomerClass() { }
private string _ID = null ; [ORMKeyField()] [ORMField("CustomerID")] [ORMIDGenerate( ORMIDGenerateStyle.GUID )] public virtual string ID { get { return _ID; } set { _ID = value; } } private string _WebSite = null;
public string WebSite { get { return _WebSite; } set { _WebSite = value; } }
private List<OrderClass> _Orders = null; [ORMRelation( ORMRelationStyle.OneToMany , "CustomerID",UniteDelete=true)] [ORMDelay()] public virtual List<OrderClass> Orders { get { return _Orders; } set { _Orders = value; } } } |
在这里Orders属性是该客户类型下所有的订单信息列表,为了性能,该属性是标记为延迟加载的。
而代码生成器在CustomerClass类型派生了新的类型,并重载了Orders属性,其演示代码如下。
public class CustomerClass_Temp : CustomerClass { public CustomerClass_Temp() { }
internal ORMFramework _Work = null;
public override List< OrderClass> Orders { get { if (base.Orders == null && this._Work != null) {
OrderClass conditionInstance = new OrderClass(); conditionInstance.Customer = this;
Array list = this._Work.ReadInstancesByCondition( conditionInstance, new string[] { "Customer" });
List< OrderClass> result = newList< OrderClass>();
foreach (OrderClass item in list) { result.Add(item); }
base.Orders = result; } return base.Orders; } set { base.Orders = value; } } }//public class TempClass |
在重载的Orders属性中,若该属性值为空则调用ORM框架来查询数据库,获得该客户名下的所有的订单信息对象,然后填充到订单列表。如此应用程序读取数据库获得一个客户信息对象时并不会导致读取该客户名下的订单信息,只有应用程序访问客户对象的Orders属性值时采用启用延迟加载。这样做能比较好的提高性能。
由于需要重载延迟调用的属性的,因此需要标记为延迟调用的属性必须是可以重载的,必须标记为abstract,virtual或override,否则代码生成器会抛出异常。
DynamicCompiler接受代码生成器生成的C#代码,调用微软.NET框架自带的C#编译器生成临时程序集,然后分析程序集获得O/R帮助器的类型并创建O/R帮助器实例。这种O/R帮助器具有很高的运行性能,动态编译技术比较好的兼顾灵活和执行性能。
Engine
这是本框架的核心模块,包含了ORMEngine和ORMContext类型。
ORMEngine是本框架的顶级对象,它调用其他程序模块,提供了O/R操作的API。
ORMContext是执行O/R操作过程的上下文信息对象。
目前就写得这么多了,以后继续。该ORM框架源代码的SVN访问地址为 https://xdesignerorm.svn.sourceforge.net/svnroot/xdesignerorm/ 。