摘要:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG】是一款专门为.Net数据库程序开发量身定做的(ORM框架)代码生成工具,所生成的程序代码基于OO、ADO.NET、分层架构、ORM及反射+工厂设计模式等。支持.Net1.1及以上版本,可用于Oracle、SqlServer、Sybase、DB2、MySQL、Access、SQLite、PostgreSQL、DM(达梦)、PowerDesigner文件、Informix、Firebird、MaxDB、Excel等和OleDb、ODBC连接的数据库并可自定义,详见文档及安装文件的示例和工具的帮助文档。
关键字: VB/C#.Net实体代码生成工具 实体代码生成工具 EntitysCodeGenerate
预期读者: 软件开发及相关人员
难度等级: 中
当前版本: 4.5
目 录
2.1 ORM框架的实现:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)
2.2.6 ORM的分析及与Xml、JSON、EString的交互
1引言
长期以来,大多数项目或产品都使用关系型数据库实现业务数据的存储,这样在开发过程中,常常有一些业务逻辑需要直接用写SQL语句实现,但这样开发的结果是:遍地布满SQL语句。这些藕合性较高的SQL语句给系统的改造和升级带来很多无法预计的障碍。也许说可以使用服务端数据库存储子程序实现,这样只是将这种耦合搬迁到后端,问题依然没有根本解决,服务端驻留过多的存储子程序也消耗着服务器的性能并给多人合作维护和更新部署带来许多障碍。为了提高项目的灵活性,特别是快速开发,ORM是一个不错的选择。举个简单的例子:在使用ORM的系统中,当数据库模型改变时,不再需要理会逻辑代码和SQL语句中涉及到该模型的所有改动,只需要将该模型映射的对象稍作改动,甚至不做改动就可以满足要求。
ORM的全称是ObjectRelational Mapping,即对象关系映射。它的实质就是将关系数据(库)中的业务数据用对象的形式表示出来,并通过面向对象(Object-Oriented)的方式将这些对象组织起来,实现系统业务逻辑的过程。在ORM过程中最重要的概念是映射(Mapping),通过这种映射可以使业务对象与数据库分离。从面向对象来说,数据库不应该和业务逻辑绑定到一起,ORM则起到这样的分离作用,使数据库层透明,开发人员真正的面向对象。下图简单说明了ORM在多层系统架构中的这个作用。
图1 ORM在多层系统架构中的作用
诚然ORM并非是万能的,面对纷繁复杂的业务逻辑,当遇到特别复杂的数据处理及海量数据运算或弥补设计的不足时还应归结到SQL或存储过程来实现才是好的选择,但它却很好地体现了“80/20(或90/10)法则”(也被称为“帕累托法则”),也就是说:花比较少(10%-20%)的力气就可以解决大部分(80%-90%)的问题,这样通过利用ORM框架,我们就仅需要付出极少数时间和精力来解决剩下的少部分问题了,这无疑缩短了整个项目开发的周期,因此快速开发、面向对象和性能优化等必须灵活兼顾才好。ORM产品应当预留适当的接口来做性能优化并对特定功能的补充支持,这样才是一个好的产品,这些该工具都提供了很好的解决方案,下文分别作一简单介绍。
2内容
2.1 ORM框架的实现:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)
好的ORM工具不仅可以帮助我们很好的理解对象,而且工具本身会帮助我们记住字段属性的业务含义并提供辅助应用。基于这个理念,一个基于.Net的ORM工具——VB/C#.Net实体代码生成工具(EntitysCodeGenerate)便应运而生,该工具运行于.Net2.0,适用性广,开发后的代码部署要求不高,在.Net更高版本上也可以很好的运行。
该工具为ORM提供了对象持久化查询和事务处理等功能,包括对象的Insert、Update、Delete、Save、Select等,对象查询提供如GetEntity及构造函数获取对象和实体集信息等。工具职责是从数据库中提取数据模型信息并生成实体类,帮助开发人员快速映射到关系数据库中的业务数据模型,最优化快速开发。目前提供直接从Oracle、SqlServer、Sybase、DB2、Access、MySQL、SQLite、PostgreSQL、DM(达梦)及OleDb、Custom(自定义)类型的数据库和PowerDesigner文件中生成VB/C#.Net代码的支持,可以生成实体和实体集的相关代码,并自动提取表及字段的注释说明和对应的数据类型等信息。
另外所生成的代码文件只需修改数据库连接,即可用于目前市场上支持ADO.NET的各种类型的数据库,如Oracle、SqlServer、Sybase、DB2、MySQL、Access、SQLite、PostgreSQL、DM(达梦)、Excel、Informix、Firebird、MaxDB和OleDb、ODBC连接的数据库等。所生成代码文件的类关系图如下所示:
工具3.x版本之后的实体层代码有个基类(BaseEntity),基类里其实也正是你项目的数据库连接的配置位置,该工具实际开发时的配置其实也就在这里了,默认为生成代码时所填写的数据库连接信息(可手工扩展修改,如从缓存或config配置文件中读取、实现对数据库连接信息的加密/解密等;大多数时候我们只需在GetConnectionString修改数据连接字符串即可):
public classBaseEntity { public static string GetConnectionString() { return "User ID=scott;Password=tiger;Data Source=oracle9";//数据库连接可修改从别处读取 } public static DatabaseType GetDatabaseType() { return DatabaseType.Oracle; //数据库连接类型设置也可修改从别处读取 } …… } |
这里设置的数据库连接类型及连接字符串是针对该命名空间下的所有实体(最初版本是放在全局设置DbConnectString这个类里面,缺陷是当一个项目有多个数据库时将不好处理;而现在通过使用基类继承及命名空间划分则很容易解决)。
System.Database.DbCore结合实体类可将简单和复杂的数据库操作及事务处理更为方便的实现,下文着重介绍在实际中的应用。
2.2在开发中的实际应用
工具安装后,在生成对应的实体代码后有个“相关配置”文件夹,里面有“配置说明”文档和相关文件,实际使用时只须按“配置说明”文档操作即可。使用该工具开发的项目,可以做到很好的切换到异构数据库,且只需变动数据库连接接口即可(即GetDatabaseType()/GetConnectionString())。同时提供了ORMaping.dll(1.0版本引用),System.Database.dll(2.0/3.x/4.x版本引用)组件;安装目录下有相应的chm格式帮助文档,是对这两个dll单独使用的说明,支持各种类型的数据库访问,并可结合生成的实体操作数据库,提供良好的事务处理等。其中ORMaping.dll支持Oracle;System.Database.dll默认支持Oracle数据库,并可用于SqlServer、Sybase、DB2、MySQL、SQLite、PostgreSQL、Informix、Firebird、MaxDB、DM(达梦)、OleDb、Odbc等。对没有直接提供的数据库可使用OleDb或Odbc连接。工具所生成的代码和提供的文件,都是可选配的,可单独使用也可配置使用。实体代码的生成界面比较简单,如下所示:
这里,只须选择数据库类型、输入正确的数据库连接字符串(Custom(自定义)的数据库类型填写正确的自定义程序集信息)、代码文件的输出目录和代码命名空间即可。实体数据类型以“数据类型映射文件”为准,工具提供了系统默认的类型映射,若有自定义的数据类型,在“数据类型映射文件”里修改,可配置到数据类型的精确刻度。数据类型初始值在“数据类型初始值文件”里修改,没有配置的取.Net默认值。若修改过程中想恢复默认的配置,只须点击右端“生成”按钮即可(注意不同语言的类型定义可能有所差异,注意区分。如:C#的byte[],在VB中的定义为byte())。准备工作做好后,单击“生成代码”按钮即可(若不想全部生成,单击“选择生成”按钮)。生成成功之后按生成配置说明文档的提示,将实体代码文件和基类文件拷贝到指定目录,并按生成代码文件所在“相关配置”目录下的配置说明文档,添加对应dll文件的引用即可。操作简单这里就不在此赘述,下面以C#.Net为例介绍实体对象的数据库操作的工作,因篇幅所限,VB.Net同理对比使用。(注:若想在.Net1.1下使用,请设置只生成实体(前后缀不填),并结合"示例代码"目录下"VS2003.rar"中的LXCT.DbCore.dll使用)
先介绍单个实体类的数据库操作。
这里以Oracle附带库DEPT为例来做说明,首先做对象的声明
DEPT entity =new DEPT(); |
此处以该对象来做阐述。
实体的操作默认以主键为准,对单个实体信息的获取可简单如下实现:
entity.DEPTNO = 50; entity = entity.GetEntity(); |
返回主键字段DEPTNO=50的对象,若数据库中没有对应的记录则返回null。DEPT表的主键字段是DEPTNO,同时对联合主键也是支持的,下文同此;GetEntity同时提供其它指定条件的重载,也可以使用GetEntityByEntityCondition,当指定条件有多个记录符合时只返回首条记录;返回多条记录可使用GetDataTable方法。
在3.3版本之后也可通过多个构造函数来获取单个实体对象的信息等:
DEPT entity =new DEPT(50); //通过主键字段条件获取实体对象 DEPT entity =new DEPT(newstring[] { "DEPTNO" },new object[] { 50 }); |
插入一个对象代码可如下所示:
entity.DEPTNO = 51; entity.DNAME = "DNAME1"; entity.LOC = "LOC1"; entity.Insert(); |
同时Insert提供多种相应的重载和InsertAll方法;区别是:Insert在插入记录时会比较对象初始的字段值,将与初始值不同的值插入,其余的以表字段的默认方式处理;InsertAll则是插入全部字段,即使是对象的初始值没有改变也会插入。
更新一个对象代码可如下所示:
entity.DEPTNO = 51; entity.DNAME = "DNAME1"; entity.LOC = "LOC1"; entity.Update(); |
Update也提供多种相应的重载和UpdateAll方法;区别是:Update在更新记录时会比较对象初始的字段值,将与初始值不同的值进行更新,其余的表字段不更新;UpdateAll则是更新全部字段,即使是对象的初始值没有改变也会将对象的初始值更新到表里。
保存是该工具新增的功能,即将插入和更新合并为一个保存方法,实体对象会自动根据主键约束解析是插入还是更新,代码如下所示:
entity.DEPTNO = 51; entity.DNAME = "DNAME1"; entity.LOC = "LOC1"; entity.Save(); |
这里是以主键为准,对多个联合主键也是支持的。保存是按主键判断的(没有主键的须指定约束字段),有就更新,没有就插入新记录。同时Save提供多种重载和SaveAll、SaveByEntityCondition方法,Save和SaveAll区别同插入和更新。
删除可如下代码所示:
entity.DEPTNO = 51; entity.Delete(); |
删除操作默认以主键为准,支持联合主键,同时也提供多种指定条件的重载方法。最后附加说明,实体对象的增删改保存操作都会返回一个int值,该值返回表中记录受影响的行数。
从这些代码可以明显的看到,这里常用的增、删、改、查操作只需简单几句即可实现,少写了很多代码!
代码可如下:
int intID = entity.GetInt32MaxID(); |
这里获取实体对象对应表字段默认第一个主键的最大值ID+1,类型为整型,同时提供GetInt?MaxID多种重载,即有整型和长整型及指定字段等重载。
除此之外,还提供了一系列的CRUD扩展方法,如:InsertEx / UpdateEx / SaveEx / DelInsert / DelInsertEx 和 实体集对象的批量操作,如:entitys.ToDataTable / Save /SaveAll/SaveEx/Delete/DelInsert/DelInsertEx/GetMaxValue/GetMinValue/GetAvgValue/GetCount/GetSqlValue/GetSqlDataSet等;详见示例代码和生成的实体代码及相关帮助文档。其中实体集对象的批量操作自动启用事务处理,操作成功统一提交,失败时统一回滚。
本节介绍的都是单实体/表无事务的操作,下节介绍多实体/表及事务处理的操作。
实体集除了提供基本的Add和索引访问方法,同时也提供了相应与数据库操作的方法,如:Save/SaveAll/SaveEx/Delete/DelInsert/DelInsertEx/DelInsertAll/ToXml/ToXml_/FromXml/FromXmlFileToDataTable和实体集对象查询。实体集对象与数据库的操作释义同单个实体,并且会自动启动事务。实体集对象查询是通过实体集构造函数获取多实体对象信息,如:
DEPTS entity =new DEPTS(true); //获取所有部门信息 EMPS entitys1 =new EMPS(newstring[] { "DEPTNO" },new object[] { 50 }); |
这里简略介绍实体对象结合System.Database.DbCore的事务处理是如何工作的,先看以下代码(可参见安装示例代码System.Database.Demo):
Entitys.Common.LC_WORKTYPE entity= new Entitys.Common.LC_WORKTYPE(); entity.ID = 1; entity.TYPENAME = "TYPENAME"; string strConnection = "Password=cc;User ID=cc;Data Source=oracle9"; DbCore dbCore = new DbCore(DatabaseType.Oracle, strConnection); dbCore.Open(); dbCore.BeginTransaction(); dbCore.Save(new Entitys.Common.LC_WORKTYPE(), entity); entity.DESCRIPTION = "类型描述"; dbCore.Save(new Entitys.Common.LC_WORKTYPE(), entity); entity.TYPENAME = "作业类型"; dbCore.Save(new Entitys.Common.LC_WORKTYPE(), entity); DataSet dst = dbCore.ExecuteDataSet("select * from lc_worktype");
entity.ID = 1; DataTable dt = dbCore.GetDataTableByEntityKey(entity); int intRecord = dbCore.Delete(entity); dt = dbCore.GetDataTableByEntityKey(entity);
dbCore.CommitTransaction(); dbCore.Close(); |
这里使用另外一个实体LC_WORKTYPE(映射到XX系统的"作业类型"这张表),BeginTransaction()为开始事务的标志;CommitTransaction()为提交当前事务;还一个是RollbackTransaction()表示回滚当前事务,放弃当前所有数据的更改。这样在事务开始和提交或回滚之间可以进行多个实体的操作,并将结果最终一起提交或回滚撤销。这里Save有两个参数第一个是实体对象的初始类用于比较实体的初始值,第二个是要保存的对象,该方法依据主键自动判断是更新还是插入;与Save类似的方法有SaveAll,同时也有重载及SaveEx、Insert、InsertAll、InsertEx、Update、UpdaAll、Update Ex、Delete、IsExitByEntityKey、Exists、Get?MaxId等方法,可相互结合使用,方法都有详尽的说明及示例代码。执行中可单步跟踪,查看该事务下每步命令执行后对应的数据集信息。
下面再看以Oracle自带的scott库为例一段代码Delete、Insert、Update并结合事务使用的代码:
DbCore dbCore = null; try { EMP entity1 = new EMP(); DataSet dst = new DataSet(); entity1.EMPNO = 7369; //设置主键EMPNO为 entity1 = entity1.GetEntity(); //取得主键EMPNO为实体对象信息 //"User ID=scott;Password=tiger;Data Source=oracle9"; dbCore = new DbCore(Entitys.Common.BaseEntity.GetConnectionString()); dbCore.Open(); dbCore.BeginTransaction(); //选择当前事务下的所有雇员EMP的信息 dst = dbCore.SelectAll().From(entity1).ExecuteDataSet(); dbCore.Delete(entity1);//删除主键EMPNO为7369的记录 dst = dbCore.SelectAll().From(entity1).ExecuteDataSet();//查看当前事务下记录,当前删除记录将不在此显示 dbCore.Insert(newEMP(), entity1);//插入刚才删除主键EMPNO为7369的记录=dbCore.Save(new EMP(), entity1); dst = dbCore.SelectAll().From(entity1).ExecuteDataSet();//查看当前事务下记录,可见刚刚插入的新记录 entity1.SAL = entity1.SAL + 100;//薪水加100 dbCore.Update(newEMP(), entity1);//更新=dbCore.Save(new EMP(), entity1); dst = dbCore.SelectAll().From(entity1).ExecuteDataSet();//查看当前事务下记录,对应薪水SAL已更新 entity1.SAL = entity1.SAL - 100;//薪水减100 dbCore.Update(newEMP(), entity1);//更新=dbCore.Save(new EMP(), entity1); dst = dbCore.SelectAll().From(entity1).ExecuteDataSet();//查看当前事务下记录,对应薪水SAL已更新 dbCore.CommitTransaction(); dbCore.Close(); } catch (Exception ex) { if (dbCore != null) { if (dbCore.IsTransaction) dbCore.RollbackTransaction();//如果已经开始事务,则回滚事务 dbCore.Close(); } } |
上面的Insert、Update方法都可以Save方法来取代,Save方法会自动判断是Update还是Insert,这里只是用来展示之用。
如一般的关系数据库所具有的查询功能一样,EntitysCodeGenerate也有着非常丰富的查询功能,如对象查询、数据集查询、函数查询、条件查询、排序查询、分组等。工具的ORM结构化实体查询(EQL)提供可以像写SQL语句一样直接查询。ECG提供ENTITYColumn类(或ENTITY.s_静态属性)帮助System.Database.DbCore.Select从数据源查询数据,Select通过ENTITYColumn实体属性(或ENTITY.s_静态属性,下同),构造查询并返回结果数据集。(EQL即EntityQuery Language)
常用实体对象查询,见上节所述,这里就不在赘述。
这里先介绍表实体原描述信息类ENTITYColumn,这里“ENTITY”为表实体类对应的占位符,其类下有实体所对应的表的表名和表字段的公共静态只读信息。其中的TableName为每个该类的对应表名,各属性字段为表名、字段名、字段对应类型的完全限定名,以键盘不能直接输入的偏僻符号’┋’为分割符拼接而成。ORM结构化查询时可使用该类的属性(或ENTITY.s_静态属性,下同)。
System.Database.DbCore类提供Select方法及其相应的重载,以SqlServer自带的pubs库为例,可见如下代码:
DataSet dst =new DataSet(); |
DataSet dst = dbCore.SelectAll().From("sales").ExecuteDataSet(); |
DataSet dst = dbCore.SelectAll().From(SALESColumn.TableName).ExecuteDataSet(); |
DataSet dst = |
DataSet dst = |
DataSet dst = dbCore.SelectAll().From(newSALES()).ExecuteDataSet(); |
其中的dbCore.SelectAll()方法缺省默认参数为查询所有字段,Select()不选择出任何字段,仅初始化实例,同时也可直接使用SQL语法的字符串或使用相应的ENTITYColumn类如SALESColumn的属性字段作为参数,或数组集合信息。也可用SelectColumn添加要查询的字段,SelectColumn(…)接受SQL表字段名字符串或ENTITYColumn类的属性字段格式的参数,SelectColumns(…)接受符合SelectColumn格式的数组参数,同时也提供SelectCustom(…)接受自定义的SQL语法的字符参数。同时鉴于SelectColumn等方法名较长,提供Add替代SelectColumn,AddMax、Min、Avg分别替代SelectColumnMax、Min、AvgValue各种同构方法,便于代码的抒写。
From(…)为查询的目标表名,这里使用的是SALESColumn.TableName,也可直接使用表名字符串,同时接受实体对象作为参数。ExecuteDataSet()执行查询并返回在内存当中的DataSet格式的结果数据集。ExecuteReader()执行查询并返回只读向前的数据结果集流IDataReader;ExecuteScalar()快速执行查询并返回第一行第一列的Object值,下同。
先见如下代码:
DataSet dst =new DataSet();DataSet dst1= new DataSet(); |
dst = dbCore.SelectAll().From().JoinInner("sales", "stor_id ", "stores","stor_id") |
dst = dbCore.SelectAll().From().JoinInner(SALESColumn.stor_id,STORESColumn.stor_id) |
dst = dbCore.SelectAll().From().JoinLeft("sales", "stor_id ", "stores","stor_id") |
dst = dbCore.SelectAll().From().JoinLeft(SALESColumn.stor_id,STORESColumn.stor_id) |
dst = dbCore.SelectAll().From().JoinRight("sales", "stor_id ", "stores","stor_id") |
dst = dbCore.SelectAll().From().JoinRight(SALESColumn.stor_id,STORESColumn.stor_id) |
dst = dbCore.SelectAll().From().JoinFull("sales", "stor_id ", "stores","stor_id") |
dst = dbCore.SelectAll().From().JoinFull(SALESColumn.stor_id,STORESColumn.stor_id) |
这里是以SqlServer系统自带pubs示例库的表sales、stores为例介绍的,JoinInner为内连接、JoinLeft为左外连接、JoinRight为右外连接、JoinFull完全外连接。参数可直接使用表名及表字段名字符串,或使用SALESColumn、 STORESColumn类,其中使用实体类静态属性字段代码可读性及维护性更好些。下面看下Where的使用。
DataSet dst =new DataSet(); |
dst = dbCore.SelectAll().From().JoinInner("sales", "stor_id ", "stores","stor_id") |
dst1 = dbCore.SelectAll().From().JoinInner(SALESColumn.stor_id,STORESColumn.stor_id) |
dst = dbCore.SelectAll().From().JoinInner("sales", "stor_id ", "stores","stor_id") |
dst1 = dbCore.SelectAll().From().JoinInner(SALESColumn.stor_id,STORESColumn.stor_id) |
dst = dbCore.SelectAll().From("sales").FromTable("stores") .Where().ConditionColumnAndEqual("sales", "stor_id", "stores","stor_id"). |
dst1 = dbCore.SelectAll().From(SALESColumn.TableName).FromTable(STORESColumn.TableName) |
这里再次展示了使用字符串和ENTITYColumn类的相互比较,可见使用ENTITYColumn更直观,可读及维护性更好些。Where()是不带参数的实例化方法,所添加的查询条件均以Condition…开头,在这里可以添加关系运算包括Equal(=), Less(<),Great(>), LessEqual(<=), GreatEqual (>=), NotEqual(<>、!=), BetweenAnd, in, not in, null, not null,like,not like和各自的关系and,or比较 及表字段与字段之间的=,<, >, <= ,>=,<>关系and,or的连接等;另外还可以添加自定义的Where条件语句等。
这里切换到Oracle数据库,以Oracle自带的scott用户为例,先看如下代码:
DataSet dst =new DataSet(); |
dst = dbCore.SelectAll().From(entity) |
dst = dbCore.SelectAll().From(entity) |
dst = dbCore.SelectAll().From(entity) |
dst = dbCore.SelectAll().From(EMPColumn.TableName) |
其中entity为雇员表EMP对应的实体对象,这里使用查询表名的地方也可直接用实体对象。OrderBy默认不带排序参数,只实例化对象,也可带排序字段,排序方式为升序;其后使用Asc添加升序字段,Desc添加降序字段。
这里同样以Oracle自带的scott用户为例,先看如下代码:
DataSet dst =new DataSet(); |
dst = dbCore.Select().SelectColumnMaxValue(EMPColumn.EMPNO) |
dst1 = dbCore.Select().AddMax(EMPColumn.EMPNO) |
|
dst = dbCore.Select().SelectColumn(EMPColumn.DEPTNO).SelectColumn(EMPColumn.SAL) .GroupBy(EMPColumn.DEPTNO).Column(EMPColumn.SAL) |
dst1 = dbCore.Select().Add(EMPColumn.DEPTNO).Add(EMPColumn.SAL) .AddMax(EMPColumn.EMPNO).AddMin(EMPColumn.EMPNO).AddAvg(EMPColumn.EMPNO) |
dst = dbCore.Select().SelectColumn(EMPColumn.DEPTNO).SelectColumn(EMPColumn.SAL) |
dst1 = dbCore.Select().Add(EMPColumn.DEPTNO).Add(EMPColumn.SAL) |
dst = dbCore.Select().SelectColumn(EMPColumn.DEPTNO).SelectColumn(EMPColumn.SAL) |
dst = dbCore.Select().SelectColumn(EMPColumn.DEPTNO).SelectColumn(EMPColumn.SAL) |
dst = dbCore.Select().SelectColumn(EMPColumn.DEPTNO).SelectColumn(EMPColumn.SAL) |
这里dst、dst1变量分别对应同样功能不同语句写法的数据集信息。同时展示了Add替代SelectColumn;AddMax、Min、Avg分别替代SelectColumnMax、Min、AvgValue的示例代码。Where和GroupBy、Having、OrderBy可同时使用,也可分开使用。最后三个展示了分别以Where和GroupBy、Having、OrderBy分开使用的例子。其中分组使用Column(…)添加分组字段。可以很明显的看出些语句的功能,和SQL语句的抒写几乎一致。如:倒数第四、五段的代码(粗体部分,两段代码功能相同,只是使用方法略异)换算成SQL语句就是(假设今天是2009-10-11):
SELECT emp.deptno, emp.sal, |
和直接编写SQL很相似,省去许多代码量,且可读性高,维护也方便。
(1)-(5)介绍的没有加入事务处理功能,下面介绍结合事务的使用,先看如下代码:
DataSet dst = new DataSet(); try { dbCore.Open();//--打开数据库连接 dbCore.BeginTransaction();//开始事务 int intRecordCount = dbCore.DeleteFrom(EMPColumn.TableName).ExecuteNonQuery(); dst = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet(); dbCore.RollbackTransaction();//回滚撤销事务 dst1 = dbCore.SelectAll().From(newEMP()).ExecuteDataSet(); dbCore.Close();//--关闭数据库连接 } catch (Exception ex) { if (dbCore != null) dbCore.Close(); MessageBox.Show(ex.Message); } |
这里仍然是以Oracle自带的scott用户为例,其中的Data Source可以选择任何Oracle的服务名。这里有必要说明的是使用了打开、关闭数据库连接,因为要使用事务所以需要先打开数据库连接,前文介绍没有使用事务的,该步骤略过了。该例的功能是先删除EMP表的所有数据再查询该表的所以信息,可看到在当前事务下是没有信息的,然后在回滚事务,再查询就又有数据了,这正是事务所起的作用。同时需要注意的是,数据库连接打开后要关闭,且当中间出现异常时也应关闭已打开的数据库连接,以释放资源。
如下代码所示:
DataSet dstSqlite = dbCore.Select().Add(Entity.Common.SQLite.t_demo.s_z) .Add(Entity.Common.SQLite.t_demo.s_a).Add("b") .From(Entity.Common.SQLite.t_demo.s_TableName) .OrderBy().Asc("z").Skip(1).Take(2).ExecuteDataSet(); int countSqlite = dstSqlite.Tables[0].Rows.Count; |
仍然以Oracle自带的scott用户为例,并结合事务处理,先看如下代码:
try { dst = dbCore.SelectAll().From(newEMP()).ExecuteDataSet(); dbCore.BeginTransaction();//开始使用事务 EMP entity = new EMP(); entity.EMPNO = 7782; intRecordCount = dbCore.Delete(entity); dst = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet(); intRecordCount = dbCore.DeleteFrom(EMPColumn.TableName) dst = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet(); intRecordCount = dbCore.DeleteFrom(new EMP()).ExecuteNonQuery(); dst = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet(); dbCore.RollbackTransaction();//回滚结束事务 dst = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet(); dbCore.Close();//--关闭数据库连接 } catch (Exception ex) { if (dbCore != null) dbCore.Close(); } |
这里先声明一个EMP雇员类的实体对象entity,并将主键EMPNO赋以值7782,然后执行Delete操作,即将主键为7782的记录删除,接下来的查询跟踪运行可见到。下面的DeleteFrom方法,并结合Where条件,将部门编号DEPTNO等于10的雇员信息全部删除,同样再下面的查询跟踪运行可看到。最后将雇员表EMP的记录全部删除之,再查询没有任何信息。之后回滚事务,再次查询,又有数据。关于使用实体对象插入(Insert)、更新(Update)、删除(Delete)、保存(Save)操作可参见(上篇)或示例代码。
仍然以Oracle自带的scott用户为例,并结合事务处理,先看如下代码:
DbCore dbCore = null; try { dbCore = PublicClass.GetNewDbCore(); dbCore.Open(); dbCore.BeginTransaction(); EMP entity = new EMP(); entity.EMPNO = 7499; entity = entity.GetEntity(dbCore); dbCore.Update(EMPColumn.TableName).Set(EMPColumn.SAL, entity.SAL + 100) .Set(EMPColumn.COMM, entity.COMM+ 100) .Set(EMPColumn.HIREDATE,DateTime.Today) .Where().ConditionAndEqual(EMPColumn.EMPNO, 7499).ExecuteNonQuery(); DataSet dst = dbCore.SelectAll().From(EMPColumn.TableName) .Where().ConditionAndEqual(EMPColumn.EMPNO, 7499).ExecuteDataSet();//查询 dbCore.Update(EMPColumn.TableName).Set(EMPColumn.SAL, entity.SAL) .Set(EMPColumn.COMM, entity.COMM) .Set(EMPColumn.HIREDATE, entity.HIREDATE) .Where().ConditionAndEqual(EMPColumn.EMPNO, 7499).ExecuteNonQuery();//恢复原值 dst = dbCore.SelectAll().From(EMPColumn.TableName) .Where().ConditionAndEqual(EMPColumn.EMPNO, 7499).ExecuteDataSet();//查询 dbCore.CommitTransaction();//提交事务 dbCore.Close(); } catch (Exception ex) { if (dbCore != null) { if (dbCore.IsTransaction) dbCore.RollbackTransaction();//如果已经开始事务,则回滚事务 dbCore.Close(); } throw ex; } |
这里先声明一个EMP雇员类的实体对象entity,并将主键EMPNO赋以值7499,然后执行获取信息到对应的实体对象,查询跟踪运行可见到。之后分别将该员工的薪水SAL和COMM分别加100和将HIREDATE更新为今天,再查询可见到刚刚更新的记录数据,然后又将该员工的SAL、 COMM和HIREDATE更新恢复原值,跟踪可见查询的数据信息,最后提交事务,该段程序只是介绍演示之用。
用法同上,不再详细赘述,这里换张表以作区别,可见示例代码,如下所示:
DbCore dbCore = null; try { dbCore = GetDbCore; dbCore.Open(); int count = dbCore.InsertInto(T_DEMO.s_TableName).Values(T_DEMO.s_C_ID,GetKeyId) .Values(T_DEMO.s_C_NAME,"NameInsert").Values(T_DEMO.s_C_IDCARD,"340221196606066066") .Values(T_DEMO.s_C_DATE,DateTime.Today).Values(T_DEMO.s_C_INT,10) .Values(T_DEMO.s_C_FLOAT,11.11).Values(T_DEMO.s_C_EIDTDATE, DateTime.Now) .ExecuteNonQuery(); dbCore.Close(); return count; } catch (Exception ex) { if (dbCore != null) dbCore.Close(); throw ex; } |
诚然ORM并不是万能的,当遇到特别复杂的数据处理、海量数据运算、性能瓶颈优化或弥补设计的不足时(尽管不是很多,但也很重要),还应归结到SQL语句或存储过程来实现才是好的选择。实际项目中仍然会有海量复杂的数据处理或复杂子查询、SQL语句优化和存储过程等,DbCore提供了良好的解决方案,很好的解决了项目中10%-20%的那部分功能。该组件支持目前市场上ADO.NET支持的各种类型的数据库,可执行自定义编写的SQL语句和存储过程等,可针对复杂功能的特殊处理并对项目瓶颈问题作性能优化等操作。
System.Database.DbCore可直接用于Oracle、SqlServer、Sybase、DB2、MySql、SQLite、PostgreSQL、Access、DM(达梦)、Informix、Firebird、MaxDB和支持OleDb、Odbc连接类型的数据库,实例代码分别如下:
DbCore dbCore =new DbCore(DatabaseType.Oracle, “OracleConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.ODPNet, “OracleConnectionString”);//ODP.NET方式 |
DbCore dbCore =new DbCore(DatabaseType.SqlServer, “SqlServerConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Sybase, “SybaseConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.DB2, “DB2ConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.MySql, “MySqlConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.SQLite, “SQLiteConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.PostgreSQL, “PostgreSQLConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Access, “AccessConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Dm, “DmConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Informix, “InformixConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Firebird, “FirebirdConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.MaxDB, “MaxDBConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.OleDb, “OleDbConnectionString”); |
DbCore dbCore =new DbCore(DatabaseType.Odbc, “OdbcConnectionString”); |
下面看一段适合Oracle和SqlServer访问的通用SQL代码:
DbCore dbCore = PublicClass.GetNewDbCore(); string strParaToken = dbCore.GetCurrentParameterToken; //对应数据库的参数前导符 string strSql = "INSERT INTO dept (deptno, dname, loc) VALUES ("+ strParaToken + "deptno, " + strParaToken + "dname, " + strParaToken+ "loc)"; dbCore.Open(); //打开数据库连接 dbCore.BeginTransaction(); //开始事务
DBCommandWrapper cmd = dbCore.GetSqlStringCommandWrapper(strSql); //cmd.AddParameter(..);//为命令增加一个参数实例 cmd.AddInParameter(strParaToken + "deptno", DbType.Int32, 99); cmd.AddInParameter(strParaToken + "dname", DbType.String, "部门名称"); cmd.AddInParameter(strParaToken + "loc", DbType.AnsiString, "locTest");
int intMaxDeptId = dbCore.GetInt32MaxId("dept","deptno");//当前表的deptno最大值
dbCore.ExecuteNonQuery(cmd); intMaxDeptId = dbCore.GetInt32MaxId("dept","deptno");//插入数据deptno=99之后当前表的deptno最大值
strSql = "DELETE dept WHERE deptno = " + strParaToken+ "deptno"; DBCommandWrapper cmd1 = dbCore.GetSqlStringCommandWrapper(strSql); cmd1.AddInParameter(strParaToken + "deptno", DbType.Int32, 99); dbCore.ExecuteNonQuery(cmd1); intMaxDeptId = dbCore.GetInt32MaxId("dept","deptno");//删除数据deptno=99之后当前表的deptno最大值
dbCore.RollbackTransaction();//回滚撤销事务。等于该方法什么都没做,只是演示作用
intMaxDeptId = dbCore.GetInt32MaxId("dept","deptno"); dbCore.Close();//关闭数据库连接 |
其中第一句的PublicClass.GetNewDbCore()方法体代码可以是newDbCore(DatabaseType.Oracle, “OracleConnectionString”)也可以是newDbCore(DatabaseType.SqlServer, “SqlServerConnectionString”),当new的是Oracle时即表示操作访问的是Oracle数据库,当new的是SqlServer即表示操作访问的是SqlServer数据库。dbCore.GetCurrentParameterToken即是获取对应数据库连接参数的前导符(如:Oracle是“:”,SQLServer是“@”等),这里也可以结合使用dbCore.StandardSqlWithParameters方法对当前带参数的SQL语句进行标准化通用处理,即所写SQL可以用于如MySql/Access数据库等。这里的数据库操作同样也是可以同实体对象一块协同工作。dbCore.GetSqlStringCommandWrapper(…)创建一个SQL语句的命令,dbCore.GetStoredProcCommandWrapper(…)创建一个执行存储过程的命令,可根据项目自身实际需要选择使用。
对专有数据库命令也可以转化为指定数据库命令来使用,这样可针对该数据库特性使用更多的方法,如Oracle、SqlServer的命令转化可像下列代码来转化:
OracleCommandWrapper cmd = dbCore.GetSqlStringCommandWrapper(strSql) as OracleCommandWrapper; |
SqlCommandWrapper cmd = dbCore.GetSqlStringCommandWrapper(strSql) as SqlCommandWrapper; |
…… |
不带参数SQL语句的快捷用法
DataSetdst = dbCore.ExecuteDataSet(strSql); DataTable dtbl = dbCore.ExecuteDataSet(strSql).Tables[0]; |
这里顺便说明一下,当程序执行出现异常时可使用dbCore.Close()来关闭当前打开的数据库连接(当然如果不显式关闭连接,DbCore在实例销毁时检测到该实例未关闭连接也会自动关闭,但这样无疑一直占据着资源,不推荐,建议及时关闭及时释放资源),如下代码所示:
catch (Exception ex) { if (dbCore != null) { if (dbCore.IsTransaction) { dbCore.RollbackTransaction();//如果已经开始事务,则回滚事务 } dbCore.Close(); } throw ex; } |
最后再说一个System.Database.DbCoreConnectLimit.AllDBMaxConnectionCount,可以设置数据库打开的最大连接数目,默认不受限制。
DbCore+存储过程标准用法:
DBCommandWrapper cmd = dbCore.GetStoredProcCommandWrapper("[包名.]存储过程名"); dbCore.ExecuteNonQuery(cmd); |
OracleCommandWrapper cmd = dbCore.GetStoredProcCommandWrapper("[包名.]存储过程名")as OracleCommandWrapper; //cmd.AddOutParameter(... //cmd.AddParameter(... //cmd.AddCursorOutParameter(... dbCore.ExecuteDataSet(cmd); |
同时DbCore也提供了对存储过程的一个快捷用法:
dbCore.ExecuteStoredProcedure("[包名.]存储过程名"); |
下面就以Oracle为例,看下DbCore+存储过程的具体写法:
DbCore dbCore = null; try { string strConnection = "Password=cc;User ID=cc;Data Source=oracle9"; DbCore dbCore = new DbCore(DatabaseType.Oracle, strConnection); dbCore.Open(); dbCore.BeginTransaction(); int count; OracleCommandWrapper cmd = dbCore.GetStoredProcCommandWrapper("storedProcedure.NameA")as OracleCommandWrapper; //cmd.AddInParameter( //cmd.AddOutParameter( //cmd.AddParameter( //cmd.AddCursorOutParameter( count = dbCore.ExecuteNonQuery(cmd); cmd = dbCore.GetStoredProcCommandWrapper("storedProcedure.NameB")as OracleCommandWrapper; //cmd.AddInParameter( count += dbCore.ExecuteNonQuery(cmd); //count += dbCore.ExecuteStoredProcedure("storedProcedure.NameC"); dbCore.CommitTransaction(); dbCore.Close(); return count; } catch (Exception ex) { if (dbCore != null) { if (dbCore.IsTransaction) dbCore.RollbackTransaction(); dbCore.Close(); } throw ex; } |
如下所示:
DbCore dbCore = new DbCore(myOracle.BaseEntity.GetConnectionString()); DataSet dst = dbCore.CreateSQL("select * from emp where deptno = :deptno") .AddInParameter("deptno",DbType.Int32, 20).ExecuteDataSet(); |
DbCore dbCore = new DbCore(myOracle.BaseEntity.GetConnectionString()); int intCount = dbCore.CreateStoredProcedure("[包名.]存储过程名") .AddInParameter("deptno",DbType.Int32, 20).ExecuteNonQuery(); |
许多工具都提供例外辅助的功能,该工具也不例外,简要介绍如下:
以Oracle自带的scott用户为例,先看如下代码:
DbCore dbCore = PublicClass.GetNewDbCore(); DataTable dt1 = dbCore.SelectAll().From(EMPColumn.TableName).ExecuteDataSet().Tables[0]; DataTable dt2 = dbCore.SelectAll().From(DEPTColumn.TableName).ExecuteDataSet().Tables[0]; DataTable dt3 = TableHelp.MergeTable(dt1, dt2,"DEPTNO");//按部门编号DEPTNO列将表dt2的数据合并到dt1 DataTable dt3_ = TableHelp.MergeTable(dt2, dt1,"DEPTNO");//按部门编号DEPTNO列将表dt1的数据合并到dt2,dt1中有多行数据对应,取首行的数据,没有对应的数据为空 DataTable dt4 = TableHelp.AddTableRowNumCol(dt3); //给dt3添加行号 DataTable dt5 = TableHelp.GetTableTopRows(dt4, 5); //获取前5行 DataTable dt6 = TableHelp.GetTableSubRows(dt4, 6, 10); //获取dt4从第6行到第10行 DataTable dt7 = TableHelp.GetTableSubRows(dt4, 11, 20);//获取dt4从第11行到第20行,注:无20行取到最后一行 DataTable dt8 = TableHelp.GetTableBottomRows(dt4, 5); //获取dt4后5行 dt8 = TableHelp.GetTableBottomRows(dt4, 50); //获取dt4后50行;dt4没有后50行,从后面往前取到最前面存在行 DataTable dt9 = TableHelp.JoinInner(dt1, dt2,"DEPTNO"); //内连接 DataTable dt10 = TableHelp.JoinInner(dt1, dt2,"deptno"); //内连接 DataTable dt11 = TableHelp.JoinLeft(dt1, dt2,"DEPTNO"); //左外连接 DataTable dt12 = TableHelp.JoinRight(dt1, dt2,"DEPTNO"); //右外连接 DataTable dt13 = TableHelp.JoinLeft(dt2, dt1,"DEPTNO"); //左外连接 DataTable dt14 = TableHelp.JoinFull(dt1, dt2,"DEPTNO"); //完全外连接
DataTable dt15 = TableHelp.SortTable(dt1,"deptno", SortDirection.Asc); DataTable dt16 = TableHelp.SortTable(dt1,"deptno"); DataTable dt17 = TableHelp.SortTable(dt1,"deptno", SortDirection.Asc,"sal",SortDirection.Asc); DataTable dt18 = TableHelp.SortTable(dt1,"deptno","sal"); DataTable dt19 = TableHelp.SortTable(dt1,"deptno", SortDirection.Desc, "sal", SortDirection.Desc); DataTable dt20 = TableHelp.SortTableDesc(dt1,"deptno", "sal"); DataTable dt21 = TableHelp.SortTable(dt1,"deptno", SortDirection.Asc, "sal", SortDirection.Desc);
TableHelp.DataTableToExcel(dt1,@"C:Documents and Settings楚涛桌面 emp1.xls"); DataSet dst = new DataSet(); DataTable dt22 = dt1.Copy(); //修改表名,DataTable默认TableName="Table",DataSet集合的DataTable.TableName不能同名 dt22.TableName = "EMP"; DataTable dt23 = dt2.Copy(); //修改表名,DataTable默认TableName="Table",DataSet集合的DataTable.TableName不能同名 dt23.TableName = "DEPT"; dst.Tables.Add(dt22); dst.Tables.Add(dt23); TableHelp.DataSetToExcel(dst,@"C:Documents and Settings楚涛桌面 emp2.xls"); |
TableHelp辅助扩展类,提供合并DataTable数据表、获取数据表指定行的数据、内连接、左外连接、右外连接、完全外连接、对数据表排序、将数据表DataTable或数据集DataSet输出到Excel文件等,可见上述程序代码的释义说明文字。
同时也提供通过过滤条件选择DataTable行、合并数据表行信息、转换数据表列值对并以DataTable的形式返回的常用方法,如下所示:
DataTable dt24 = TableHelp.GetTableSelect(dt1,"deptno=10");//选取deptno=10的所有信息,并以DataTable的形式返回 DataTable dt25 = TableHelp.GetTableSelect(dt1,"deptno=20");//选取deptno=20的所有信息,并以DataTable的形式返回 DataTable dt26 = TableHelp.TableAppend(dt24, dt25);//将dt23数据按行附加到dt22,并以新的结果数据表的形式返回 string[,] strArray = new string[1, 2]; strArray[0, 0] = "SCOTT"; strArray[0, 1] = "scott/tiger"; DataTable dt27 =TableHelp.ReplacleTableColValue(dt26,"ename", strArray); |
MergeTable和TableAppend区别是,前者是横向合并,即列的合并;后者是纵向合并,即行的合并。ReplacleTableColValue功能既是如字面意思所示替换Table列值,没有匹配的保留原值,功能类似Oracle的decode函数。
CommonHelp提供了许多常用方法,如财务上人民币金额大写的转换、字符串中中文的检查、唯一随机数字固定长度为20的数字字符串的产生、HTML代码和对应格式化的字符串的相互转化、整型和浮点型数字字符串检查、Email地址和日期时间格式字符串的检查、获得中文字符串的汉语拼音码首字母等常用功能,可见以下示例:
string str1 = CommonHelp.NumberToRMB(1); //"壹元整" str1 = CommonHelp.NumberToRMB(102); //"壹佰零贰元整" str1 = CommonHelp.NumberToRMB(1000234); //"壹佰万零贰佰叁拾肆元整" str1 = CommonHelp.NumberToRMB(1000023456); //"壹拾亿零贰万叁仟肆佰伍拾陆元整" str1 = CommonHelp.NumberToRMB(100000234567); //"壹仟亿零贰拾叁万肆仟伍佰陆拾柒元整" decimal dec = 1234007890123.45M; str1 = CommonHelp.NumberToRMB(dec); //"壹万贰仟叁佰肆拾亿零柒佰捌拾玖万零壹佰贰拾叁元肆角伍分" string str = string.Empty; for (int i= 0; i < 1000; i++) { str += CommonHelp.GetOnlyID() + " ";//唯一随机数字固定长度为20的数字字符串 } MessageBox.Show(str); str = CommonHelp.GetID20();//唯一随机数字固定长度为的数字字符串 str = CommonHelp.GetID25();//唯一随机数字固定长度为的数字字符串 str = CommonHelp.GetID30();//唯一随机数字固定长度为的数字字符串 string str1 = "abcdEFGH"; bool isHasChinese = CommonHelp.IsHasChineseWord(str1); //false不含有中文字符 str1 = "abcd啊EFGH"; isHasChinese = CommonHelp.IsHasChineseWord(str1); //true含有中文字符 |
还有其他常用方法和加密/解密常用方法扩展类CryptographyHelp、OfficeHelp常用方法辅助扩展类等就不在此一一列举了。
2.2.6 ORM的分析及与Xml、JSON、EString的交互
ORM通过对开发人员隐藏SQL细节可以大大提高生产力。然而很不幸,“ORM”和“性能问题”常常一起出现,它容易产生一些未被发现的荒谬查询,虽然几率不是很高,但万一出现,如果没有好的分析方案,也是极为头疼的事。通常情况下,数据库管理员可以通过如交叉引用有问题的存储过程或其它途径来查找问题代码。但是,ORM依赖于动态生成的SQL,便很难这么做了。所以,需要一些有效的ORM分析方法。该工具组件System.Database.DbCore提供了DbCore.GetCurrentCommandText等静态方法可以在任何执行ORM操作时分析当前执行的SQL语句,这样在任何时候当出现异常时可查看当前执行的SQL,如果SQL正常可排除不是ORM的问题,否则说明所编写的代码结构中出现了问题。
工具生成的实体提供ToXml和FromXml两个方法及相应的重载和补充方法,可以方便实体对象与XML内容直接相互转换。
示例代码如下所示:
TSTENTITY entity = new TSTENTITY(1); //得到主键为1的实体对象信息 TSTENTITY entity_ = new TSTENTITY();//仅实例化实体对象 string sTst = entity.ToXml(); entity.ToXml(@"C: st.xml", Encoding.UTF8, Formatting.Indented); entity.ToXml_(@"C: st1.xml", Encoding.UTF8, Formatting.Indented); entity_ = entity_.FromXml(sTst);
entity_ = new TSTENTITY(); entity_ = entity_.FromXmlFile(@"C: st.xml"); entity_ = new TSTENTITY(); entity_ = entity_.FromXmlFile(@"C: st1.xml"); |
同样实体集也提供ToXml和FromXml及相应的重载和补充方法,方便实体集对象与XML内容直接相互转换。
示例代码如下所示:
employeeS entitys1 = new employeeS(true); //获取员工对象的所有信息到实体集对象 employeeS entitys2 = new employeeS();//仅实例化实体集对象 employeeS entitys3 = new employeeS(); employeeS entitys4 = new employeeS();
string strXml = entitys1.ToXml(); entitys2 = entitys2.FromXml(strXml); //从Xml内容中加载对象
strXml = entitys1.ToXml(Formatting.Indented); entitys3 = entitys3.FromXml(strXml); //从Xml内容中加载对象
string strFile = "temp.xml"; entitys1.ToXml_(strFile, Encoding.UTF8, Formatting.Indented); entitys4 = entitys4.FromXmlFile(strFile); //从Xml文件中加载对象 |
工具生成的实体提供ToJSON和FromJSON两个方法及相应实体集方法,可以方便实体对象、实体集对象与JSON内容直接相互转换。
示例代码如下所示:
employee e1 = new employee("A-C71970F");//得到主键为A-C71970F的对应数据 employee e2 = new employee("asd");//没有对应数据 employeeS es = new employeeS(true);//获取全部数据 employee entity1; employeeS entitys; employeeS entitys1; employeeS entitys2;
entity1 = new employee(); string json1 = e1.ToJSON();//将实体信息转化为JSON文本 entity1 = entity1.FromJSON(json1);//从JSON文本中转化到实体
entitys = new employeeS(); entitys1 = new employeeS(); entitys2 = new employeeS(); entitys.Add(e1); entitys.Add(e2); string json3 = entitys.ToJSON(); entitys1 = entitys1.FromJSON(json3); //通用用法 entitys2.FromJSON(json3); //实体集专用用法
entitys1 = new employeeS(); entitys2 = new employeeS(); json3 = es.ToJSON(); entitys1 = entitys1.FromJSON(json3); //通用用法 entitys2.FromJSON(json3); //实体集专用用法 |
工具生成的实体提供ToString和FromString两个方法及相应实体集方法,可以方便实体对象、实体集对象与String内容直接相互转换。
示例代码如下所示:
employee e1 = new employee("A-C71970F");//得到主键为A-C71970F的对应数据 employeeS es = new employeeS(true);//获取全部数据 employee entity1 = new employee(); employeeS entitys1 = new employeeS(); employeeS entitys2 = new employeeS(); string str1 = e1.ToString();//转化为字符串文本 entity1 = entity1.FromString(str1);//从字符串文本中实例化到实体 string strEntitys = es.ToString(); entitys1 = entitys1.FromString(strEntitys);//实体集通用用法 entitys2.FromString(strEntitys);//实体集专用用法 |
注:EString是针对实体/集(亦即一维/二维数据)设计的,格式为键值对的形式出现,以键盘不能直接输入的单个生僻字符’┋’为分割符,对于集合类,以’┋┋’(即连续两个’┋’)为分割符(示例=>name┋value1┋┋name┋value2)。
实体集对象默认是可以支持Linq的,只是Linq是在.NET Framework 3.5 中出现的技术,所以在创建新项目的时候必须要选3.5或者更高版本,才能使用。当前项目基本上都是.Net3.5或以上版本的,这里也把这部分示例代码加上:
// Entitys Linq 的查询示例 net framework 3.5/4.0/及其以上 TSTENTITYS entitys = new TSTENTITYS(); TSTENTITY entity = new TSTENTITY(); entity.TSTTXT = "aa"; entitys.Add(entity); entitys.Add(newTSTENTITY() { TSTTXT = "bb" });
var qq = entitys.OfType<TSTENTITY>(); var pp =from p in qq where p.TSTTXT =="aa" select p; foreach (var pin qq) { string s = p.TSTTXT; //两个结果分别是:aa、bb } foreach (var pin pp) { string s = p.TSTTXT; //筛选后的结果:aa } |
可以看出关键是这句entitys.OfType<TSTENTITY>(),OfType是System.Linq下的方法,需要引用该命名空间。选择3.5或更高版本的.NET Framework之后,创建的新项目中会自动包含System.Linq的命名空间(using System.Linq;)。
经常,客户说报错的时候,作为开发人员,基本上不可能到第一现场观察错误提示,这也导致了异常定位非常的困难,大大增加了维护的成本。使用日志后,一旦某个页面出错,即使客户没有反馈,系统也能记录下来,为管理员修正错误以及优化系统提供足够丰富的信息。ECG同样也提供了轻量级的简单日志组件。
配置说明如下:
配置节关键字 |
配置说明 |
_LOG_LEVEL_ |
日志级别配置节关键字,配置内容可为:DEBUG|INFO|WARN|ERROR|FATAL (默认DEBUG) |
_LOG_FILE_ |
日志文件配置节关键字配置内容:日志文件路径+文件名(不含文件扩展名)(默认SimpleLogger) |
_LOG_FILE_MAX_SIZE_ |
日志文件配置节关键字,日志文件的最大长度(单位:字节)(不得小于1024字节,即1K)(默认1M = 1*1024*1024=1048576) |
_LOG_SPLIT_ |
日志文件是否按命名空间分开存储的配置节关键字: TRUE|FALSE (默认FALSE) |
_LOG_NS_MAXLENGTH_ |
当日志文件按命名空间分开存储时(即_LOG_SPLIT_=TRUE),命名空间截取的最大长度(该值必须大于0,方有效),默认命名空间全称 |
_LOG_NS_DIV_COUNT_ |
当日志文件按命名空间分开存储时(即_LOG_SPLIT_=TRUE),命名空间长度最大截取到第几分段(以“.”分割)(该值必须大于0,方有效),默认命名空间全称 |
_LOG_ASYN_ |
异步写入日志标示,TRUE:新启线程采用日志队列方式异步写入,FALSE:直接写入日志文件(默认FALSE) |
_LOG_ASYN_WAIT_ |
异步写入时,当日志队列为空的等待毫秒数(必须大于等于0,为0则没有等待时间(较耗资源);可不配置,默认100毫秒)(_LOG_ASYN_必须配置TRUE,该项才起作用) |
_LOG_ASYN_THREAD_BACKGROUND_ |
异步写入线程是前台线程还是后台线程(对应Thread.IsBackground属性),TRUE后台线程,FALSE前台线程(默认FALSE)(_LOG_ASYN_必须配置TRUE,该项才起作用) |
这里的配置节关键字是区分大小写的,要求全部大写,所有配置项都有默认值,不需要全部配置,可根据实际需要进行选择。配置项在系统首次加载时进行初始化,再次修改配置项需重启应用程序。
日志路径默认为运行程序所在运行路径,对于Web程序,若在VisualStudio下web.config中没有配置_LOG_FILE_指定到具体路径下,则会在对应的IDE目录下(以VS2005为例,在Microsoft Visual Studio8Common7IDE);建议这里明确填写具体完整的绝对路径(例如:C:LogSimpleLogger,日志记录到C:Log目录下(如果目录不存在则会自动创建),日志文件名以SimpleLogger为前缀开头);日志文件的扩展名为(.log)。
using System.Logger; |
SimpleLogger logger =SimpleLogger.GetInstance(); logger.Debug("环境打印出的调试日志"); logger.Info("环境打印出的信息日志"); logger.Warn("环境打印出的警告日志"); logger.Error("环境打印出的一般错误日志"); logger.Fatal("环境打印出的致命错误日志"); |
日志组件位于System.Database.dll中,项目引用命名空间(System.Logger)即可,日志组件提供五种级别的日志记录(DEBUG|INFO|WARN|ERROR|FATAL),最低DEBUG,最高FATAL,当配置的级别大于等于对应级别时,会自动记录日志,如:当前配置_LOG_LEVEL_值为ERROR时,DEBUG|INFO|WARN三种级别日志将直接跳过不记录,ERROR|FATAL级别的日志会记录。SimpleLogger提供了单实例接口SimpleLogger.GetInstance(),支持多线程并发操作,使用时可直接调用获取日志实例对象。同时各级别日志记录接口(Debug| Info Warn| Error| Fatal)均有不同重载方式。
3结束语
以上就是VB/C#.Net实体代码生成工具(EntitysCodeGenerate)对各种数据库访问的统一操作的简单介绍,详细可见示例代码及工具帮助文档;工具安装后附带大量的示例代码,里面有更多的示例和chm格式的帮助文档。另外,使用该工具,当项目需要切换数据库或使用不同的数据库时,只须适当修改数据库连接类型及连接字符串即可。
在设计表时建议:数据库表名及字段名应当遵循通用规范,如都使用大写英文字母,首字符以大写英文字母开头且不含有特殊符号,如'┋'字符;表中不含有嵌套表、表名不与字段名重复(对老系统如果表名与字段名相同,则可通过建立视图或同义词再修改实体类名也可,但一般不推荐这样使用)等。另外文字命名可约定俗成为单数形式,可以是英文字母及数字和下划线(_)组合,此外建议表命名以"T_"开头,表字段以"C_"开头,视图以"V_"开头等,这样做的目的可使数据库设计更规范化,也避免了与关键字的冲突,同时也是因为许多数据库都是英文字母开头的且不能含有特殊字符,如Oracle就是这样,且表名称和字段名都是大写(对PostgreSQL建议采用小写命名),长度不超过30个字符(命名过长无意义,有些数据库也不支持),多出的信息可以在命名注释里多写些注释信息。若采用Oledb、Odbc方式连接,表字段名最好不要这样FIELD1,2,3...以数字顺序结尾,因为这样容易与参数机制冲突,导致一些数据访问时带来一些参数上不必要的麻烦,且这样命名也不是好的规范,必要时可以是FIELDA,B,C...。每个表建议都设置主键,可以是联合主键。若没有主键,则在使用主键判断记录的唯一性时须指定字段。
当前也有许多类似的工具(如LINQ To SQL)等,但总有许多限制:如LINQ To SQL只在.Net3.5之上才能使用,仅用于SqlServer;其它也有许多相关不足,如:对国产数据库及自定义数据库的不支持、无主键或联合主键的不支持、大量复杂的配置、属性字段注释提取的缺少、运行不稳定、生成代码可读性差、同一项目不支持多数据库的整合、需要为不同数据库学习不同的编程方式、内部配置文件复杂或报错、工具兼容性差、缺少ORM分析或其它使用中及生产环境运行的问题等等。该工具很好的解决了这些问题,支持.NET1.1及以上版本,并结合多年具体项目的需求,可以像抽屉一样单独或分开使用,也可和其它组件并行使用,如可以与其它组件或平台的集成,动态得从相关平台或组件中读取数据库参数而不必写死在文件里,这样若要实现对用户数据库连接信息加密/解密也可以很好的处理。
理论的实现总是从简单到复杂,覆盖所有可能,实际应用则需要结合实际从复杂到简单,凡是要灵活变通使用,化复杂为简单,将复杂的东西以简单的方式处理。
软件开发都希望从大量重复的代码中解放出来,缩短工期并提高项目质量,同时当需要的时候可以准确的将绝大多数后台代码快速完成,并要便于后期维护,这些就需要适当借助一些工具和方法为开发、维护和交流提供良好的保障,显然该工具提供了最佳选择。
尽管我们注入大量心血,但不足之处在所难免,有待进一步完善,敬请来信交流(lxchutao@163.com)、批评斧正!
4 相关下载地址
http://www.cnblogs.com/lxchutao/archive/2011/06/01/2065977.html
http://files.cnblogs.com/lxchutao/EntitysCodeGenerate.rar
http://www.cnblogs.com/lxchutao
http://www.skycn.com/soft/53715.html
http://download.enet.com.cn/html/030212009031901.html
http://www.newhua.com/soft/98941.htm
http://www.duote.com/soft/32643.html
http://www.crsky.com/soft/25626.html
http://download.csdn.net/source/3030634
http://qun.qq.com/air/#60873348/share