先有设计,后有代码,改设计总比改代码更容易一些,改设计的成本更低廉,软件也要按图纸施工,没有图纸的建筑物,将来也不好维护,没有数据库设计的软件更怎么可能好维护呢?
1:总希望自己的程序能兼容多种数据库,那就尽量用多种数据库都兼容的数据库字段类型来设计表结构。
2:将数据库中的表结构能对应到C#的类型里,然后能读取相应的实体字段、按实体的属性写入数据库表中。
3:为了测试大容量并发插入表的测试,设计了2个表结构,其中一个用GUID做主键,生成唯一主键,另一个用自增量或者序列做主键。
4:代码生成器里,设置了数据库类型与C#的数据类型的对应关系设置及默认值的关系设置等。
代码 /// <summary> /// 数据库类型映射关系(数据库类型、C#类型、默认值、读取函数) /// </summary> string [,] DataTypeMapping = { { " NVARCHAR " , " String " , " null " , " ToString " }, { " CHAR " , " String " , " null " , " ToString " }, { " INT " , " int " , " 0 " , " ToInt " }, { " INTEGER " , " int " , " 0 " , " ToInt " }, { " NUMERIC " , " Double " , " 0 " , " ToDouble " }, { " FLOAT " , " Double " , " 0 " , " ToDouble " }, { " DATE " , " DateTime? " , " null " , " ToDateTime " }, { " BLOB " , " Byte[] " , " null " , " ToByte " }, { " BFILE " , " Byte[] " , " null " , " ToByte " }, { " IMAGE " , " Byte[] " , " null " , " ToByte " } }; /// <summary> /// 获取字段的类型 /// </summary> /// <param name="fieldDataType"> 数据库字段类型 </param> /// <returns> 类型 </returns> private string GetDataType( string fieldDataType, ref string defaultValue) { // 这是默认值 string returnValue = typeof ( string ).Name.ToString(); defaultValue = " null " ; // 这个是差找对比 for ( int i = 0 ; i < DataTypeMapping.GetLength( 0 ); i ++ ) { if (fieldDataType.IndexOf(DataTypeMapping[i, 0 ]) >= 0 ) { returnValue = DataTypeMapping[i, 1 ]; defaultValue = DataTypeMapping[i, 2 ]; // 不循环了,提高效率 break ; } } return returnValue; } private string GetDataType( string fieldDataType) { // 这是默认值 string returnValue = typeof ( string ).Name.ToString(); // 这个是差找对比 for ( int i = 0 ; i < DataTypeMapping.GetLength( 0 ); i ++ ) { if (fieldDataType.IndexOf(DataTypeMapping[i, 0 ]) >= 0 ) { returnValue = DataTypeMapping[i, 1 ]; // 不循环了,提高效率 break ; } } return returnValue; } private string GetColumnDataType(XmlNode xmlNode, string columnName) { string fieldDataType = string .Empty; for ( int i = 0 ; i < xmlNode.ChildNodes.Count; i ++ ) { if (((XmlNode)xmlNode.ChildNodes[i]).LocalName.Equals( " Columns " )) { for ( int j = 0 ; j < xmlNode.ChildNodes[i].ChildNodes.Count; j ++ ) { string field = xmlNode.ChildNodes[i].ChildNodes[j].ChildNodes[ 2 ].InnerText; if (field.Equals(columnName)) { for ( int z = 0 ; z < xmlNode.ChildNodes[i].ChildNodes[j].ChildNodes.Count; z ++ ) { if (xmlNode.ChildNodes[i].ChildNodes[j].ChildNodes[z].LocalName.Equals( " DataType " )) { // 字段类型大写 fieldDataType = xmlNode.ChildNodes[i].ChildNodes[j].ChildNodes[z].InnerText.ToUpper(); break ; } } break ; } } break ; } } return GetDataType(fieldDataType); } private string GetConvertFunction( string fieldDataType) { // 这是默认值 string returnValue = " ToString " ; // 这个是差找对比 for ( int i = 0 ; i < DataTypeMapping.GetLength( 0 ); i ++ ) { if (fieldDataType.IndexOf(DataTypeMapping[i, 0 ]) >= 0 ) { returnValue = DataTypeMapping[i, 3 ]; // 不循环了,提高效率 break ; } } return returnValue; }
5:接下来需要用代码生成器生成相应的代码,这时候比较麻烦的事情有以下几个:
A:guid做主键时,为了兼容多种数据库,用了字符类型字段,用自增量时,主键为整数类型,这里需要有一定的技术水平需要处理一下。
B:用自增量做主键时,不同的数据库的处理方式不一样,这时候需要考虑写同一套代码,能兼容多种数据库的问题,程序的写法是一样同时能能支持多种数据库。
6:为了测试多种数据库上的兼容性,现在以 Oracle、SQLServer 为主要目标来进行测试,创建数据库的脚本如下:
UserByGUID.Oracle.SQL (Oracle按GUID为主键模式)
代码 -- Create table create table USERBYGUID ( ID NVARCHAR2( 50 ), FULLNAME NVARCHAR2( 50 ), SALARY NUMBER( 10 , 2 ) default 0 , AGE NUMBER( 2 ) default 0 , BIRTHDAY DATE, PHOTO BLOB, ALLOWEDIT NUMBER( 1 ) default 1 , ALLOWDELETE NUMBER( 1 ) default 1 , ENABLED NUMBER( 1 ) default 1 , DELETEMARK NUMBER( 1 ) default 0 , DESCRIPTION NVARCHAR2( 200 ), CREATEDATE DATE default sysdate, CREATEUSERID NVARCHAR2( 50 ), CREATEUSERREALNAME NVARCHAR2( 50 ), MODIFYDATE DATE default sysdate, MODIFYUSERID NVARCHAR2( 50 ), MODIFYUSERREALNAME NVARCHAR2( 50 ) );
UserByInt.Oracle.SQL (Oracle按序列为主键模式)
代码 -- Create sequence create sequence SEQ_USERBYINT minvalue 1 maxvalue 999999999999999999999999999 start with 21 increment by 1 cache 20 ; -- Create table create table USERBYINT ( ID NUMBER, FULLNAME NVARCHAR2( 50 ), SALARY NUMBER( 10 , 2 ) default 0 , AGE NUMBER( 2 ) default 0 , BIRTHDAY DATE, PHOTO BLOB, ALLOWEDIT NUMBER( 1 ) default 1 , ALLOWDELETE NUMBER( 1 ) default 1 , ENABLED NUMBER( 1 ) default 1 , DELETEMARK NUMBER( 1 ) default 0 , DESCRIPTION NVARCHAR2( 200 ), CREATEDATE DATE default sysdate, CREATEUSERID NVARCHAR2( 50 ), CREATEUSERREALNAME NVARCHAR2( 50 ), MODIFYDATE DATE default sysdate, MODIFYUSERID NVARCHAR2( 50 ), MODIFYUSERREALNAME NVARCHAR2( 50 ) );
UserByGUID.SQLServer.SQL (SQLServer按GUID为主键模式)
代码 CREATE TABLE [dbo].[UserByGUID]( [Id] [nvarchar]( 50 ) NOT NULL, [FullName] [nvarchar]( 50 ) NULL, [Salary] [numeric]( 10 , 2 ) NULL DEFAULT (( 0 )), [Age] [ int ] NULL DEFAULT (( 0 )), [Birthday] [smalldatetime] NULL DEFAULT (getdate()), [Photo] [p_w_picpath] NULL, [AllowEdit] [ int ] NULL DEFAULT (( 1 )), [AllowDelete] [ int ] NULL DEFAULT (( 1 )), [Enabled] [ int ] NULL DEFAULT (( 1 )), [DeleteMark] [ int ] NULL DEFAULT (( 0 )), [Description] [nvarchar]( 50 ) NULL, [CreateDate] [smalldatetime] NULL DEFAULT (getdate()), [CreateUserId] [nvarchar]( 50 ) NULL, [CreateUserRealname] [nvarchar]( 50 ) NULL, [ModifyDate] [smalldatetime] NULL DEFAULT (getdate()), [ModifyUserId] [nvarchar]( 50 ) NULL, [ModifyUserRealname] [nvarchar]( 50 ) NULL, CONSTRAINT [PK_USERBYGUID] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
UserByInt.SQLServer.SQL (SQLServer自增量方式)
代码 CREATE TABLE [dbo].[UserByInt]( [Id] [ int ] IDENTITY( 1 , 1 ) NOT NULL, [FullName] [nvarchar]( 50 ) NULL, [Salary] [numeric]( 10 , 2 ) NULL DEFAULT (( 0 )), [Age] [ int ] NULL DEFAULT (( 0 )), [Birthday] [smalldatetime] NULL DEFAULT (getdate()), [Photo] [p_w_picpath] NULL, [AllowEdit] [ int ] NULL DEFAULT (( 1 )), [AllowDelete] [ int ] NULL DEFAULT (( 1 )), [Enabled] [ int ] NULL DEFAULT (( 1 )), [DeleteMark] [ int ] NULL DEFAULT (( 0 )), [Description] [nvarchar]( 50 ) NULL, [CreateDate] [smalldatetime] NULL DEFAULT (getdate()), [CreateUserId] [nvarchar]( 50 ) NULL, [CreateUserRealname] [nvarchar]( 50 ) NULL, [ModifyDate] [smalldatetime] NULL DEFAULT (getdate()), [ModifyUserId] [nvarchar]( 50 ) NULL, [ModifyUserRealname] [nvarchar]( 50 ) NULL, CONSTRAINT [PK_USERBYINT] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
7:用代码生成器,生成相关的类代码:
8:代码复制到工程文件里的效果如下:
9:编写自动化测试程序如下
代码 // ------------------------------------------------------------ // All Rights Reserved , Copyright (C) 2010 , Jirisoft , Ltd. // ------------------------------------------------------------ using System; using System.IO; namespace DotNet.Web.Permission { using DotNet.Utilities; /// <remarks> /// SQLServerExample /// 多种数据库兼容的实现方法,只写一套程序在多种数据库上执行例子程序 /// /// 修改纪录 /// /// 版本:1.0 2010.06.20 JiRiGaLa 写好例子程序方便别人学习。 /// /// 版本:1.0 /// <author> /// <name> JiRiGaLa </name> /// <date> 2010.06.20 </date> /// </author> /// </remarks> public partial class SQLServerExample : System.Web.UI.Page { protected void Page_Load( object sender, EventArgs e) { string id = string .Empty; // 按GUID方式添加数据 id = this .AddEntityByGUID(); Page.Response.Write( " :GUID: " + id + " <br> " ); // 按自增量方式添加数据 id = this .AddEntityByIdentity(); Page.Response.Write( " :Identity: " + id + " <br> " ); } /// <summary> /// 读取图片文件 /// </summary> /// <param name="fileName"> 文件名 </param> /// <returns> 字节 </returns> private byte [] GetFile( string fileName) { FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); BinaryReader binaryReader = new BinaryReader(fileStream); byte [] file = binaryReader.ReadBytes((( int )fileStream.Length)); binaryReader.Close(); fileStream.Close(); return file; } /// <summary> /// 添加实体 /// </summary> /// <returns> 主键 </returns> private string AddEntityByGUID() { // 这个是管理器 UserByGUIDManager userByGUIDManager = new UserByGUIDManager(); // 不要自增量 // userByGUIDManager.Identity = false; // 这个是实体 UserByGUIDEntity userByGUIDEntity = new UserByGUIDEntity(); userByGUIDEntity.Id = BaseBusinessLogic.NewGuid(); userByGUIDEntity.FullName = " 吉日嘎拉 " ; userByGUIDEntity.Salary = 15000.00 ; userByGUIDEntity.Age = 33 ; userByGUIDEntity.AllowDelete = 1 ; userByGUIDEntity.AllowEdit = 1 ; userByGUIDEntity.Birthday = new DateTime( 1978 , 5 , 19 ); userByGUIDEntity.DeleteMark = 0 ; userByGUIDEntity.Enabled = 1 ; userByGUIDEntity.Photo = this .GetFile( @" C:\Users\jirigala\Pictures\01.jpg " ); // 这里是添加操作 return userByGUIDManager.Add(userByGUIDEntity); } /// <summary> /// 添加实体 /// </summary> /// <returns> 主键 </returns> private string AddEntityByIdentity() { // 这个是管理器 UserByIntManager userByIntManager = new UserByIntManager(); // 是要自增量 // userByIntManager.Identity = true; // 不用返回 // userByIntManager.ReturnId = false; // 这个是实体 UserByIntEntity userByIntEntity = new UserByIntEntity(); userByIntEntity.FullName = " 吉日嘎拉 " ; userByIntEntity.Salary = 15000.00 ; userByIntEntity.Age = 33 ; userByIntEntity.AllowDelete = 1 ; userByIntEntity.AllowEdit = 1 ; userByIntEntity.Birthday = new DateTime( 1978 , 5 , 19 ); userByIntEntity.DeleteMark = 0 ; userByIntEntity.Enabled = 1 ; userByIntEntity.Photo = this .GetFile( @" C:\Users\jirigala\Pictures\01.jpg " ); // 这里是添加操作 return userByIntManager.Add(userByIntEntity, true ); } } }
10:同样的程序,修改配置文件,连接到Oracle数据库上,GUID方式生成主键、徐列生成主键完全运行正常,惊喜一下。
代码 <? xml version = " 1.0 " ?> < configuration > < appSettings > < add key = " RunMode " value = " Local " /> < add key = " ServiceFactory " value = " ServiceFactory " /> < add key = " ServicePath " value = " DotNet.Service " /> < add key = " DbHelperAssmely " value = " DotNet.DbUtilities " /> < add key = " DataBaseType " value = " Sqlserver " /> < add key = " DbHelperClass " value = " DotNet.DbUtilities.SqlHelper " /> < add key = " UserCenterConnection " value = " Server=JIRIGALA-PC;Database=UserCenterV30;Uid=sa;Pwd=sa; " /> <!-- < add key = " DataBaseType " value = " Oracle " /> < add key = " DbHelperClass " value = " DotNet.DbUtilities.OracleHelper " /> < add key = " UserCenterConnection " value = " Data Source = FDAZTC;user id = ztc;password = ztc " /> --> < add key = " BusinessDBConnection " value = " Data Source=JIRIGALA-PC\SQLEXPRESS;Initial Catalog=ConvenienceServices;Integrated Security=SSPI; " /> < add key = " CustomerCompanyName " value = " 权限学习 " /> < add key = " SoftName " value = " Water " /> < add key = " SoftFullName " value = " 通用权限管理系统 " /> < add key = " CompanyFullName " value = " 杭州吉日软件 " /> < add key = " Update " value = " 2010.06.20 " /> < add key = " BugFeedback " value = " JiRiGaLa_Bao@hotmail.com " /> </ appSettings > < connectionStrings > < add name = " Sqlserver " connectionString = " Server=JIRIGALA-PC;Database=UserCenterV30;Uid=sa;Pwd=sa; " providerName = " DotNet.DbUtilities.SqlHelper " /> </ connectionStrings > < system.web > < compilation debug = " true " > </ compilation > < authentication mode = " Windows " /> < customErrors mode = " Off " /> </ system.web > </ configuration >
11:PowerDesigner设计的PDM文件下载,请在此下载
12:完整的例子程序下载,请在此下载,若有缺少的DLL引用,可以在从目录DotNet.Web.Permission\Resource\External 里进行重新饮用
13:在淘宝网店地址 上购买相应的功能软件,此强大的代码生成器仅销售RMB:100元、服务后,请添加技术支持QQ:2520 – 56973,索取相应的程序源码、设计文档等等。
吉日嘎拉(蒙古语为吉祥如意),2000年毕业于黑龙江大学计算机系软件专业,目前定居杭州,典型的IT软件土鳖一个,外号“软件包工头”。
通用权限管理系统组件(GPM - General Permissions Manager)自2003年开始发布,目前是国内注册用户和免费盗版用户最多的权限管理系统,是各种信息管理系统开发中彻底的权限解决方案。本组件支持多种主流数据库(Oracle、sqlsever、db2、mysql),功能强大,使用方便,代码简洁,思路严谨,被广大支持者称为权限管理系统中的“走火入魔级权限管理系统”。
精心维护通用权限管理系统组件(GPM - General Permissions Manager)有8年多,3年的不断推广,20万行经典的业务逻辑积累,经过上万次的调试修正,经历了四百个付费客户,上百软件公司的实战开发。
11年以上开发经验,外企工作5年,上市公司3年,独立经营软件公司2年,主持研发部门管理工作4年以上。
将权限管理、工作流做到我能力的极致,一个人只能做好那么很少的几件事情。
QQ:252056973,Mail:jirigala_bao@hotmail.com
通用权限管理模块的严谨设计定位、精心编码实现、不断维护推广、持续优化改进,主要是为了实现一个可以高度重复利用劳动成果的工具软件并×××给所需的人们,另想成为国人值得骄傲的知名软件功能模块。