c#轻量级ORM框架实现(⼀)
发布⼀个⾃⼰写的⼀个轻量级ORM框架,本框架设计期初基于三层架构.所以从命名上来看,了解三层的朋友会很好理解.
设计该框架的⽬的:不想重复的写增删改查,把精⼒放到功能实现上.
发布改框架的原因:希望给初学者⼀个参考,希望能给予好的建议,给⾃⼰⼀个展⽰机会.
在我开始之前,先说明⼀下,我对"软件⼯程学"概念东西⼏乎不通,最⾼⽂化程度:初⼆,所以不喜勿喷.
开始我的orm设计最底层
最底层的是⼀个DalBase,它是⼀个抽象的,实现了增删改查的基本操作.
它既然是⼀个抽象的,那么它的内部就不应该有任何具体成员.
它内部核⼼对象有:访问数据库的对象,⽣成sql⽂的对象的抽象定义,创建sql参数的抽象⽅法,where参数化查询对象的抽象定义.
/
// <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();
如需扩展⽀持的数据库种类,只需要分别对这⼏个抽象对象进⾏具体代码的实现.
以下代码是增删改(查询相对来说⽐较复杂,单独放出来)
/// <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;
//这句话其实内部实现访问了 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,以及分页的⽅法jquery框架定义
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的⽅法.
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 ,这两个是表的映射.如下:
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并指定了连接字符串.
那两个表就⼿⼯建⼀下吧,连接字符串可以直接指定写死喽
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()
: base(DatabaseTypeEnum.SQLServer, ConnStr)
{ }
}
BLLCommon指定了连接字符串和数据库类型,如果⼀个项⽬使⽤多个(或多种)数据库,可以创建多个BLLCommon,
BLLBase的所有⽅法都定义的虚⽅法,所以在这⾥可以重写你要改的东西,来完成业务逻辑的限制或约束.
如,我想要在增加Test1表数据时,Remark赋值'aa'
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);
}
下⾯是调⽤代码:
此代码实现了,新增和更新,及事务的使⽤⽅法.
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是最合适的,这样如果切换数据库不会受任何影响. wh1.Add("字段1")
.Equal(1)
.And("字段2")
.EqualNot(2);
这个是收到jquery的启发,来设计的类,每⼀个操作都返回了当前对象(即WhereHelper),也就是说,它可以⽆限的"点"下去.
使⽤它的最⼤好处是:你不⽤考虑参数名的重复,不⽤考虑换库的兼容性,操作起来是如此简单.
关于WHereHelper的使⽤及设计思想请看:
费劲的写了这么多,如果有⼈看有⼈有要求,或有什么建议,请留⾔.
昨天看了⼀篇关于程序员的⽂章,⼀个优秀的程序员6⼤特质,其中之⼀是:懂的分享.
把这套框架源码共享下
下载说明:
本框架没有⽤于过任何商业⽤途(当然以后就不⼀定了)
欢迎指正,批评,优化,建议,抄袭及商业使⽤
共享的⽬的是为了优化⼀下框架,接收⼀下建议,了解下不⾜.
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论