来源: c# 轻量级ORM框架 实现(一) – 老张一笑 – 博客园
发布一个自己写的一个轻量级ORM框架,本框架设计期初基于三层架构.所以从命名上来看,了解三层的朋友会很好理解.
设计该框架的目的:不想重复的写增删改查,把精力放到功能实现上.
发布改框架的原因:希望给初学者一个参考,希望能给予好的建议,给自己一个展示机会.
在我开始之前,先说明一下,我对”软件工程学”概念东西几乎不通,最高文化程度:初二,所以不喜勿喷.
开始我的orm设计最底层
最底层的是一个DalBase,它是一个抽象的,实现了增删改查的基本操作.
它既然是一个抽象的,那么它的内部就不应该有任何具体成员.
它内部核心对象有:访问数据库的对象,生成SQL文的对象的抽象定义,创建SQL参数的抽象方法,where参数化查询对象的抽象定义.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/// <summary> /// 访问数据的DBHelper对象 /// </summary> public abstract DbHelperBase DBHelper { get ; } //子类实现 /// <summary> /// 获得生成sql文本的对象 /// </summary> protected internal abstract BuildSQL BuildSQLTextObj { get ; } /// <summary> /// 获得数据参数对象 /// </summary> protected internal abstract DbParameter GetDbParam( string paramName, object paramValue); /// <summary> /// 创建WhereHelper对象 /// </summary> internal abstract WhereHelper CreateWhereHelper(); |
如需扩展支持的数据库种类,只需要分别对这几个抽象对象进行具体代码的实现.
以下代码是增删改(查询相对来说比较复杂,单独放出来)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
/// <summary> /// 执行sql语句返回所影响行数 /// </summary> public virtual int ExecuteBySQL( string sqlText, Dictionary< string , object > dbParams) { //执行sql语句的操作都由此方法实现 DbParameter[] parameters = GetDbParam(dbParams); int rows = DBHelper.ExecNonQuery(sqlText, parameters); return rows; } /// <summary> /// 新增1条或多条 /// </summary> public virtual int Add<TModel>( params TModel[] models) where TModel : ModelBase, new () { ThrowModelIsNullException(models); string sqlText; Dictionary< string , object > dbParams;<br> //这句话其实内部实现访问了 BuildSQLObj 的 InsertSQLTExtAndParam ,它生成sql语句和sql参数对象,把它又写成方法是为了让子类还可以对它进行重写,这个或许之前想多了,直接重写Add也是可以的. GenerateInsertSQLTextAndParam( out sqlText, out dbParams, models); int rows = ExecuteBySQL(sqlText, dbParams); return rows; } /// <summary> /// 更新一条或多条记录,根据sql条件,指定更新字段(sqlWhere为空,则按主键更新) /// </summary> public virtual int Update<TModel>( string [] fields, string sqlWhere, Dictionary< string , object > dbParams, params TModel[] models) where TModel : ModelBase, new () { ThrowModelIsNullException(models); string sqlText; GenerateUpdateSQLTextAndParam( out sqlText, ref dbParams, sqlWhere, fields, models); int rows = ExecuteBySQL(sqlText, dbParams); return rows; } /// <summary> /// 更新一条或多条记录,根据sql条件,指定忽略更新的字段 /// </summary> public virtual int UpdateByIgnoreField<TModel>( string [] ignoreFields, string sqlWhere, Dictionary< string , object > dbParams, params TModel[] models) where TModel : ModelBase, new () { string [] allFields = BuildSQLTextObj.GetAllFields<TModel>(); string [] updateFields = BuildSQLTextObj.RemoveFields(allFields, ignoreFields); return Update(updateFields, sqlWhere, dbParams, models); } /// <summary> /// 根据主键删除记录 /// </summary> public virtual int Delete<TModel>( params object [] pkValues) where TModel : ModelBase, new () { string sqlText; Dictionary< string , object > dbParams; if (pkValues == null || pkValues.Length < 0) { throw new DbDataException( "删除操作时主键为空!" ); } GenerateDeleteSQLTextAndParam<TModel>( out sqlText, out dbParams, pkValues); int rows = ExecuteBySQL(sqlText, dbParams); return rows; } |
查询提供了,3中方法,返回DataSet,返回List,返回DataReader,以及分页的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public virtual DataTable GetDataTable<TModel>( string sqlWhere, Dictionary< string , object > dbParams, string [] orderFields, bool isDesc, params string [] selectFields) where TModel : ModelBase, new () { string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataTableBySQL(sqlText, dbParams); } public virtual DbDataReader GetDataReader<TModel>( string sqlWhere, Dictionary< string , object > dbParams, string [] orderFields, bool isDesc, params string [] selectFields) where TModel : ModelBase, new () { string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataReaderBySQL(sqlText, dbParams); } public virtual List<TModel> GetList<TModel>( string sqlWhere, Dictionary< string , object > dbParams, string [] orderFields, bool isDesc, params string [] selectFields) where TModel : ModelBase, new () { DbDataReader dbReader = GetDataReader<TModel>(sqlWhere, dbParams, orderFields, isDesc, selectFields); List<TModel> list = GetListByDataReader<TModel>(dbReader); return list; } |
为什么有个DataReader方法呢,返回它有两个用处,1是把它转换成List,2因为DataSet的获取内部也是调用了DataReader方法,(通过反编译可以看到的)
因为DataReader 转换 List 要比 DataTable to List的效率要快;
DalBase的的基本功能其实就差不多了,下边来介绍下BLLbase的实现,BLLBase主要实现了dal方法的一些重载而已,
从字面意思来说,业务逻辑的基类,我这里定义了业务逻辑的规则,比如Insert前(后)的事件,查询前的事件,等等..
BLLBase里增删改其实和DalBase并无特别差别,就不介绍了.
主要说下Get的方法.
1
2
3
4
5
6
7
8
|
public virtual DataTable GetDataTable<TModel>( string [] selectFields, string [] orderFields, bool isDesc, WhereHelper dbWhereModel) where TModel : ModelBase, new () { StringBuilder sqlWhere = null ; Dictionary< string , object > dbParam = null ; GetSqlWhereParam( ref sqlWhere, ref dbParam, dbWhereModel); return GetDataTable<TModel>(sqlWhere.ToString(), dbParam, orderFields, isDesc, selectFields); } |
这是一个带where条件的查询,返回datatable,其它获取List,DataReader,方法都是一样的,WhereHelper这个类的创建,我自豪了很久,在下面将调用时会举例它的使用及实现.
举个测试例子:
1.创建一个WinForm程序,引用 ZhCun.Framework.Common 和ZhCunFramework.DataAccess
2.创建Models文件夹,分别建Test1.cs和Test2.cs ,这两个是表的映射.如下:
1
2
3
4
5
6
7
8
9
10
11
|
namespace ZhCun.Framework.WinTest.Models { public class Test1 : ModelBase { [ModelAttribute(IsPrimaryKey = true , IsIdentity = true )] public int Id { set ; get ; } public string Name { set ; get ; } public string Age { set ; get ; } public string Remark { set ; get ; } } } |
映射的Model必须继承ModelBase,这就是为什么在DalBase里面加泛型约束的原因,其实ModelBase我并没有想好用它来实现什么,就当个限制条件吧.
另外 ModelAttribute 特性,指定该属性的映射数据库表的类型及其它规则,这里Id表示是一个自增长的主键.
3.创建一个BLLCommon 类,这个类名或许叫什么 XXXXServer ,或许更好一些,它继承了BLLBase并指定了连接字符串.
那两个表就手工建一下吧,连接字符串可以直接指定写死喽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class BLLCommon : BLLBase { static string ConnStr { get { // "Data Source=192.168.0.55;Initial Catalog=aa;uid=sa;pwd=123"; return Configurations.GetConnectString( "Test" ); } } public BLLCommon() { } } |
BLLCommon指定了连接字符串和数据库类型,如果一个项目使用多个(或多种)数据库,可以创建多个BLLCommon,
BLLBase的所有方法都定义的虚方法,所以在这里可以重写你要改的东西,来完成业务逻辑的限制或约束.
如,我想要在增加Test1表数据时,Remark赋值’aa’
1
2
3
4
5
6
7
8
9
10
11
12
|
public override int Add<TModel>( params TModel[] models) { foreach ( var item in models) { Test1 m = item as Test1; if (m != null ) { m.Remark = "aa" ; } } return base .Add<TModel>(models); } |
下面是调用代码:
此代码实现了,新增和更新,及事务的使用方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
BLLCommon _BllObj = new BLLCommon(); private void btnAdd_Click( object sender, EventArgs e) { Test1 t1 = new Test1(); Test2 t2 = new Test2(); t1.Name = txtName.Text; t1.Age = txtAge.Text; t1.Remark = txtRemark.Text; t2.Name = txtName.Text; t2.Age = txtAge.Text; t2.Remark = txtRemark.Text; try { _BllObj.TransStart(); _BllObj.Add(t2); _BllObj.Add(t1); var model = _BllObj.GetModel<Test1>(1); if (model != null ) { model.Remark = "更新时间:" + DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ); _BllObj.Update( new string [] { "Remark" }, model); } _BllObj.TransCommit(); MessageBox.Show( "提交成功!" ); } catch (Exception ex) { _BllObj.TransRollback(); MessageBox.Show(ex.Message); } } |
带where查询的方法调用:
WhereHelper wh1 = _BllObj.CreateWhereHelper(); wh1.Add("Name").Equal("张三"); List<Test1> list1 = _BllObj.GetList<Test1>(wh1);
WhereHelper对象为啥要用BLLObj来创建,这个解释一下,考虑到不同数据库的查询应该有不同的语法,把WhereHelper抽象出来,也是为了扩展.
引用BLLBase的时候指定了数据库,所以BLL是知道创建哪中数据库的WhereHelper的,所以用BLL对象来创建WhereHelper是最合适的,这样如果切换数据库不会受任何影响.
1
2
3
4
|
wh1.Add( "字段1" ) .Equal(1) .And( "字段2" ) .EqualNot(2); |
这个是收到JQuery的启发,来设计的类,每一个操作都返回了当前对象(即WhereHelper),也就是说,它可以无限的”点”下去.
使用它的最大好处是:你不用考虑参数名的重复,不用考虑换库的兼容性,操作起来是如此简单.
关于WHereHelper的使用及设计思想请看: 轻量级ORM框架 之 WhereHelper (二)
费劲的写了这么多,如果有人看有人有要求,或有什么建议,请留言.
昨天看了一篇关于程序员的文章,一个优秀的程序员6大特质,其中之一是:懂的分享.
把这套框架源码共享下 下载
下载说明:
本框架没有用于过任何商业用途(当然以后就不一定了)
共享的目的是为了优化一下框架,接收一下建议,了解下不足.