上一遍我介绍了AgileEAS.NET中统计的数据访问,本文我将来介绍AgileEAS.NET中的ORM实现思路。关于ORM的概念和介绍,我在此就不在重复介绍了,我相信大家都很熟悉这个概念了。
AgileEAS.NETORM并没有采用如NHibernate中映射文件的文件的模式,而是采用了直接硬编码的模式实现,ORM体系设计采用了属性/列>数据对象>数据集合(表)的结构:
最基本的思路是一个记录/实体(IEntity)映射一条记录,一个实体包括若干属性/列(Colunm),而一组IEntity和一组Colunm组成一个数据表对象(IEntity),用于对象一个表、视图、或者一个查询结果,下面我涉及到的类、接口介绍一下:
Column
相当于一个数据库表的一个列。组织于数据库与数据库表行中,由1-n个列组成一行数据或一个数据库表,包含了标题、名称、数据类型、数据库列名、大小、值表达式、是否自动增长、值、默认值等属性。这些属性方便用户在定义数据库实体类及表类时使用,在数据库属性类中,常规情况下,一个属性等同于一个数库列,但是,一个属性并不一定关联一个数据库列,也可以关联到一个函数或常量之上。
IEntity
实体接口相关于关系数据库中的一个数据库表行,把一个数据库表行映射库一个数据库实体类, 以下是他的详细定义:
IEntity /// <summary> /// 定义数据库记录的映射对象接口,该接口为对旬关系映射提供支持,实现本接口的对象映谢为一条数据库记录。 /// </summary> public interface IEntity : EAS.Sessions.ISessionObject, IGetValue, ISetValue, IDataObject, ISerializable { /// <summary> /// 获取IEntity对象所属于的ITable对象的类型。 /// </summary> System.Type TableType { get;} /// <summary> /// 复制一个新的IEntity对象。 /// </summary> /// <returns> Entity 对象的复制。</returns> IEntity DeepClone(); /// <summary> /// 获取IEntity对象的列集合,我们定义了表/行/列的数据表格模型,目前是以表行为结构进行整理的。 /// </summary> ColumnCollection Columns { get;} /// <summary> /// 获取IEntity对象的属性集合,列集合(Columns)的同义词。 /// </summary> ColumnCollection Propertys { get;} /// <summary> /// 获取IEntity对象中的主键集合。 /// </summary> ColumnCollection PrimaryKeys { get;} /// <summary> /// 获取或设置IEntity对象对应的行ID。 /// </summary> System.Data.DataRow DataRow { get;set;} /// <summary> /// 获取一个值,该值指示从上次调用构造函数、Refresh方法以来,内存中的对象属性值是否曾经改变。调用Save 方法后,应该将该属性设置为false。 /// </summary> bool Changed { get ;} /// <summary> /// 获取一个值,该值指示当前对象在数据库中是否存在。默认实现总是返回false。 /// </summary> bool Exists { get ;} /// <summary> /// 实体的状态,即对于数据源来说,指示实现数据是否要同步到数据库。 /// </summary> EntityState State { get; set;} /// <summary> /// 实体所在的表,实体对象可以是独立于表对象、也可依附于一个表对象这上。 /// </summary> ITable Table { get;set;} /// <summary> /// 获取数据对象存储的表的名称。 /// </summary> /// <remarks>该值可以是完全限定名称。名称比较不区分大小写。</remarks> string DbTableName { get; } /// <summary> /// 获取或者设置指定索引位置的属性值。 /// </summary> /// <exception cref="ArgumentException">不存在指定的属性。</exception> /// <exception cref="ArgumentNullException">试图将主键属性的值设置为空值。</exception> /// <remarks>名称比较不区分大小写。</remarks> object this[int index] { get; set;} /// <summary> /// 获取或者设置指定名称的属性值。 /// </summary> /// <exception cref="ArgumentException">不存在指定的属性。</exception> /// <exception cref="ArgumentNullException">试图将主键属性的值设置为空值。</exception> /// <remarks>名称比较不区分大小写。</remarks> object this[string name] { get; set;} /// <summary> /// 获取对象中已经建立的所有属性的数目。 /// </summary> int PropertyCount { get; } /// <summary> /// 获取一个值,该值指示对象是否存在指定名称的属性。 /// </summary> /// <param name="name">要确定的属性的名称。名称比较不区分大小写。</param> /// <returns>返回属性是否存在的指示。</returns> /// <exception cref="ArgumentNullException">name 为空引用或者空字符串。</exception> bool ContainsProperty(string name); /// <summary> /// 获取一个值,该值指示对象是否存在指定标题的属性。 /// </summary> /// <param name="caption">要确定的属性的标题。标题比较不区分大小写。</param> /// <returns>返回属性是否存在的指示。</returns> /// <exception cref="ArgumentNullException">caption 为空引用或者空字符串。</exception> bool ContainsProperty2(string caption); /// <summary> /// 获取指定索引位置处的属性对象。 /// </summary> /// <param name="index">属性索引。</param> /// <returns>返回在对象属性列表中指定位置处的属性对象。</returns> Property GetProperty(int index); /// <summary> /// 获取指定属性名称的属性对象。 /// </summary> /// <param name="name">属性名称。名称比较不区分大小写。</param> /// <returns>返回具有指定名称的Property 对象。</returns> /// <exception cref="ArgumentNullException">name 为空引用或者空字符串。</exception> Property GetProperty(string name); /// <summary> /// 获取指定属性标题的属性对象。 /// </summary> /// <param name="caption">属性标题。名称比较不区分大小写。</param> /// <returns>返回具有指定标题的Property 对象。</returns> /// <exception cref="ArgumentNullException">caption 为空引用或者空字符串。</exception> Property GetProperty2(string caption); /// <summary> /// 保存当前数据对象。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>根据当前数据对象的值更新相应数据库记录的值。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Save(); /// <summary> /// 插入当前数据对象。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>根据当前数据对象的值向数据库插入一条数据库记录。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Insert(); /// <summary> /// 更新当前数据对象。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>根据当前数据对象的值更新相应数据库记录的值。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Update(); /// <summary> /// 刷新当前数据对象。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>从数据库中加载当前数据对象的值。</remarks> void Refresh(); /// <summary> /// 缓存刷新,根据实体对象依附的表对象缓存刷新实体数据。 /// </summary> void CacheRefresh(); /// <summary> /// 实在是否在数据库存在。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>从数据库中加载当前数据对象的值。</remarks> void ExistsInDb(); /// <summary> /// 从数据库中删除当前数据对象(对应的数据库记录)。 /// </summary> /// <exception cref="InvalidOperationException"> DbConnection 为空引用、DbTableName 为空、还没有定义对象的属性、还没有定义对象的主键属性(更新时)、或者主键属性的值为空值。</exception> /// <remarks>删除后仍然可以使用Save 方法将对象再次保存到数据库。</remarks> void Delete(); }
数据库实体接口和数据表接口是ORM中最核心的一个接口,为什么说他是最核心的接口呢,他是ORM映射中的数据实体对象(Object)、他和关系数据库中的关系(表)进行直接的映射、一个数据库表行就是一个只有一个元素的关系(即只有一条记录的表)、数据表是数据库表行的一个纵行扩展。 数据库实体接口中实现了,和数据库表行映射所需求的属性集合,同时也提供了Refresh、Insert、Update、Save、Delete数据库持久化操作和一个CacheRefresh方法。
Refresh方法是数据实体对象从关系数据库表行同步自身的一个方法,他从关系数据库表中取出指定行数据,同步内在中的数据实现对象。
Save方法是数据实体对象根据把自己同步到关系数据库表中的一个方法,当数据库表中存在这条数据行是,修改数据库表中的这一行,如果数据库表行中不存在这一行,则向数据库表中插入这一行。
Insert方法不进行判读直接向数据库插入数据。
Update方法不进行判读直接更新数据库记录。
Delete方法是数据实体对象从关系数据库删除与数据实体对象映射的那一行数据。
CacheRefresh方法同Refresh只是不从数据库而是从缓存。
ITable
数据表相关于关系数据库中的一个数据库表,把一个数据库表映射库一个数据表, 以下是他的详细定义:
ITable /// <summary> /// 定义数据表接口,该接口为对旬关系映射提供支持。 /// </summary> /// <remarks>此按口定义了对象关系映射中有关于数据表的描述。</remarks> public interface ITable : EAS.Sessions.ISessionObject, System.ComponentModel.IListSource, IDataObject, ISerializable { /// <summary> /// 获取或设置包含有特定数据的对象。 /// </summary> object Tag { get;set;} /// <summary> /// 获取ITable 对象拥有的IEnrity 对象的类型。 /// </summary> System.Type EntityType{ get;} /// <summary> /// 获取Table对象的列集合,我们定义了表/行/列的数据表格模型,目前是以表行为结构进行整理的。 /// </summary> ColumnCollection Columns { get;} /// <summary> /// 获取Table对象中的实体记录集(Entity集合),我们定义了表/行/列的数据表格模型。 /// </summary> EntityCollection Rows { get;} /// <summary> /// 获取Table对象中的主键集合。 /// </summary> ColumnCollection PrimaryKeys { get;} /// <summary> /// 获取数据对象存储的表的名称。 /// </summary> /// <remarks>该值可以是完全限定名称。名称比较不区分大小写。</remarks> string DbTableName { get; } /// <summary> /// 获取一个值,指定数据表中的数据是否发生改变,如果发生改变,则会考虑数据是否同步到数据源中。 /// </summary> bool Changed { get;} /// <summary> /// 获取一个值,指示集合中已经修改过的Entity 对象总数,即此数据表对象所包含的数据行数。 /// </summary> int ChangedCount{ get;} /// <summary> /// 获取或设置表中数据实体对象的数据存储方式。 /// </summary> StorageMode StorageMode{get;set;} /// <summary> /// 获取进行数据操作的数据访问对象,此处的数据库访问对像可能会是不同的访问对象,数据库访问环境、数据库访问者、分布式数据访问对象等。 /// </summary> EAS.Data.Access.IDataAccessor DataAccessor { get; set;} /// <summary> /// 获取表中指定行索引处的实体。 /// </summary> IEntity this[int index] { get;} #region 数据/缓存表 /// <summary> /// 获取与表对象对应的数据集对象。 /// </summary> System.Data.DataTable DataTable { get;} /// <summary> /// 获取与表对象对应的数据集对象(缓存)。 /// </summary> System.Data.DataTable CacheDataTable { get;} #endregion /// <summary> /// 创建数据查询条件。 /// </summary> /// <returns>为表成功创建的一个条件对象。</returns> Condition CreateCondition(); /// <summary> /// 创建一条新记录,没有同步到数据库。 /// </summary> /// <returns>为本成功成功创建的行实体对象。</returns> IEntity CreateEntity(); /// <summary> /// 向表中添加一条记录,没有同步到数据库。 /// </summary> /// <param name="enrity">数据实体对象。</param> void AddEntity(IEntity enrity); /// <summary> /// 更新表中的一条记录,没有同步到数据库。 /// </summary> /// <param name="enrity"> Entity 对象。</param> void UpdateEntity(IEntity enrity); /// <summary> /// 更新表中的一条记录,没有同步到数据库。 /// </summary> /// <param name="index"> Entity 对象在集合中的索引。</param> void UpdateEntity(int index); /// <summary> /// 删除表中的一条记录,没有同步到数据库。 /// </summary> /// <param name="enrity"> Entity 对象。</param> void DeleteEntity(IEntity enrity); /// <summary> /// 删除表中的一条记录,没有同步到数据库。 /// </summary> /// <param name="index"> Entity 对象在集合中的索引。</param> void DeleteEntity(int index); /// <summary> /// 从数据库中删除数据表记录。 /// </summary> /// <remarks>删除操作所影响的数据行数。</remarks> int Delete(); /// <summary> /// 从数据库中删除数据表记录。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <remarks>删除操作所影响的数据行数。</remarks> int Delete(Condition condition); /// <summary> /// 查询,从数据库读取Table 对象所对应数据库实体数据,刷新同步数据表对象源到数据库对象表。 /// </summary> void Query(); /// <summary> /// 查询,从数据库读取Table 对象所对应数据库实体数据,刷新同步数据表对象源到数据库对象表。 /// </summary> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> void Query(int top); /// <summary> /// 条件查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> void Query(Condition condition); /// <summary> /// 条件查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> void Query(Condition condition,int top); /// <summary> /// 缓存查询,从当前数据表缓存查询实体数据。 /// </summary> void CacheQuery(); /// <summary> /// 缓存查询,从当前数据表缓存查询实体数据。 /// </summary> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> void CacheQuery(int top); /// <summary> /// 缓存查询,从当前数据表缓存查询指定条件的实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> void CacheQuery(Condition condition); /// <summary> /// 缓存查询,从当前数据表缓存查询指定条件的实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> void CacheQuery(Condition condition,int top); /// <summary> /// 使用本表缓存刷新指定的实体对象。 /// </summary> /// <param name="entity">本表对应的实体对象。</param> void CacheRefresh(IEntity entity); /// <summary> /// 查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetDataTable(); /// <summary> /// 查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetDataTable(int top); /// <summary> /// 条件查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetDataTable(Condition condition); /// <summary> /// 条件查询,从数据库读取Table 对象所对应数据库实体数据。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetDataTable(Condition condition,int top); /// <summary> /// 缓存查询,从表中缓存(DataTable)的数据表记录中查询。 /// </summary> /// <returns>返回缓存的数据表对象。</returns> System.Data.DataTable GetCacheDataTable(); /// <summary> /// 缓存查询,从表中缓存(DataTable)的数据表记录中查询。 /// </summary> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetCacheDataTable(int top); /// <summary> /// 缓存查询,从表中缓存(DataTable)的数据表记录中查询。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetCacheDataTable(Condition condition); /// <summary> /// 缓存查询,从表中缓存(DataTable)的数据表记录中查询。 /// </summary> /// <param name="condition">数据查询条件,为空引用则返回整个数据库实体(表、视图、表值存储过程)。</param> /// <param name="top">Top查询,即一次最多返回多少条数据记录,返回多少个数据实体。</param> /// <returns>查询返回的数据表对象。</returns> System.Data.DataTable GetCacheDataTable(Condition condition,int top); /// <summary> /// 保存当前数据,即把表中变更过的数据同步到数据库,不使用事务。 /// </summary> /// <remarks>根据当前数据对象的值更新相应数据库记录的值。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Save(); /// <summary> /// 保存当前数据,即把集合中的数据同步到数据库,不使用事务。 /// </summary> /// <param name="force">是否强制同步到数据库。</param> /// <remarks>根据当前数据对象的值更新相应数据库记录的值。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Save(bool force); /// <summary> /// 保存当前数据,即把集合中的数据同步到数据库。 /// </summary> /// <param name="force">是否强制同步到数据库。</param> /// <param name="transaction">数据同步过程中是否使用事务。</param> /// <remarks>根据当前数据对象的值更新相应数据库记录的值。如果没有为对象添加任何属性,则不会产生任何效果。</remarks> void Save(bool force,bool transaction); /// <summary> /// 根据索引及指定长度取得表中部分记录,组成一个新表。 /// </summary> /// <param name="startIndex">索引开始位置。</param> /// <param name="length">需要取得的实体数。</param> /// <returns>结果实体组成的新表。</returns> ITable GetRangeTable(int startIndex,int length); }
ITable中我们定义了Columns列集合、Rows行集合,同时也提供了Query、Save、Delete数据库持久化操作方法和缓存查询CacheQuery。
Query方法是数据表对象从关系数据库表同步自身的一个方法,他从关系数据库表中取出指定行数据,同步内存中的数据表对象。
Query方法在执行过程中,可以一次全部同步数据库表数据,也可以根据条件同步数据库的某一部分数据,在进行条件参数时,需求使用到查询条件对象(Condition)、查询条件单元对象(Element)和结果排序单元对象(OrdeElement)、由这三个对象组合成复杂的查询条件,通过Query方法查询指定条件的数据。
Save方法和Delete方法实现和IEntity定义中的有相似的功能,在此先不作介绍。
CacheQuery实现从缓存同步数据行。
在上面ORM的对象架构中,涉及到两个集合类EntityCollection、ColumnCollection在此文不做特别说明,详细请参考开发包中的类库帮助,下面我说一下ORM中的查询条件。
我们知道在进行数据库操作中要进行条件查询,我们把行、列、表都进行了对象映射,那么SQL条件怎么办,AgileEAS.NET中定义了三个类,查询条件(Condition)、组成条件的元素(Element)、排序条件元素。
Condition
条件类是ORM中的一个功能辅助类,他相当于开发人员在编号SQL语句的过程中所编写的一组查询条件。条件由条件单元组件,如果条件用于查询,在查询时,需要对查询结果排序,刚需求使用排序条件单元,以下是条件类及条件单元的结构关系:
上图为条件、条件单元(Element)、排序单元(OrdeElement)之间的类关系图,条件元素()由方法关联条件单元及排序单元,条件单元(Element)表示的是一个很简单的条件元素,比如 NAME = ‘james’, 排序单元(OrdeElement)也只是表示一个很简单的排序单元,比如:NAME或 NAME DESC,但是在我们进行的企业应用开发中,条件都是很复杂的,比如有这样的条件 NAME = ‘james’ And SEX = ‘男’ And Age < 16,这样的怎么么写,我们使用条件单元组成复杂的条件。这个条件我们使用ORM进行映射:
Condition condition = table.CreateCondition ();
condition.AddElement(new Element(“NAME”,”james”);
condition.AddElement(new Element(“SEX”,”男”);
condition.AddElement(new Element(“Age”,” 16”);
这样就完成了这个条件的定义,在条件的组合及定义条件的过程中,我们就有一个认识,条件并不是光有等值比较,还包括有很多其他的条件类型,同样,条件的组合不光是And 还有Or组合,下图是ORM条件映射中的两个辅助枚举,列举条件类型及条件组合类型。
ElementType
条件类型枚举,定义某个条件的类型,比如=,<,like之类的:
ElementType /// <summary> /// Condition 类的数据元素(Element)类型枚举。 /// </summary> public enum ElementType { /// <summary> /// “=”比较指定值与指定属性 /// </summary> EqualTo = 0x00000000, /// <summary> /// “>”比较指定值与指定属性 /// </summary> GreaterThan = 0x00000001, /// <summary> /// “>=”比较指定值与指定属性 /// </summary> GreaterThanAndEqualTo = 0x00000002, /// <summary> /// “(不等于)”"比较指定值与指定属性 /// </summary> NotEqualTo = 0x00000003, /// <summary> /// “(小于)”比较指定值与指定属性 /// </summary> LessThan = 0x00000004, /// <summary> /// “小于等于”"比较指定值与指定属性 /// </summary> LessThanAndEqualTo = 0x00000005, /// <summary> /// (BetWeen),介于两个介之间。 /// </summary> BetWeen=0x00000010, /// <summary> /// (NotBetWeen),不介于两个介之间。 /// </summary> NotBetWeen=0x00000020, /// <summary> /// 指定子字符串与指定属匹配(模式匹配%value%) /// </summary> Match = 0x00000100, /// <summary> /// 指定子字符串与指定属匹配(前缀匹配value%) /// </summary> MatchPrefix = 0x00000200, /// <summary> /// 指定子字符串与指定属匹配(后缀匹配%value) /// </summary> MatchSuffix = 0x00000300, /// <summary> /// 指定子字符串与指定属不匹配(%value%)。 /// </summary> NotMatch = 0x00000400, /// <summary> /// 指定子字符串与指定属不匹配(前缀不匹配value%)。 /// </summary> NotMatchPrefix = 0x00000500, /// <summary> /// 指定子字符串与指定属不匹配(后缀不匹配%value)。 /// </summary> NotMatchSuffix = 0x00000600, /// <summary> /// 指定子字符串中的字匹配(字匹配value__value)。 /// </summary> WordMatch = 0x00000700, /// <summary> /// 指定子字符串中的字不匹配(字匹配value__value)。 /// </summary> NotWordMatch = 0x00000800, /// <summary> /// 与给定的列表list里的值匹配。 /// </summary> In = 0x00001000, /// <summary> /// 与给定的列表list里的值不匹配。 /// </summary> NotIn = 0x00002000, /// <summary> /// 直接构造字符串,传入就是条件元素。 /// </summary> SqlCondition = 0x00008000 }
在这些定义中,有一个特殊的条件类型,SqlCondition条件类型,我们在进行条件映射时,现实中的数据总是复杂的,有很多无法直接使用各种条件映射出,或者,通过单条件映射组件条件很复杂,我们可以直接使用SQL语句作为条件,在这个时间,就可以使用SqlCondition条件类型。它为我们保留了编写优质高效SQL语名的接口。
ElementCombineType
两个条件或者两条条件元素的组合方式,OR或者AND
/// <summary> /// Condition 类的数据元素(Element)之间的组合方法。 /// </summary> public enum ElementCombineType { /// <summary> /// And,两个Element 对象之间是以And 为组合条件。 /// </summary> And = 0x0000, /// <summary> /// Or,两个Element 对象之间是以Or 为组合条件。 /// </summary> Or = 0x0001 }
至此,ORM组织体系大概介绍完成,不过在此文中,并未对ORM的数据访问机制,关于这部分我在下文介绍。
QQ群:15118502