zoukankan      html  css  js  c++  java
  • c# 轻量级ORM框架 实现(一)

    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<stringobject> 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<stringobject> 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<stringobject> 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<stringobject> dbParams, paramsTModel[] 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<stringobject> 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<stringobject> dbParams, string[] orderFields, boolisDesc, 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<stringobject> dbParams, string[] orderFields, boolisDesc, 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<stringobject> 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<stringobject> 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 { setget; }

            public string Name { setget; }

            public string Age { setget; }

            public string Remark { setget; }

        }

    }

     映射的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()

                base(DatabaseTypeEnum.SQLServer, ConnStr)

            { }

    }

     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大特质,其中之一是:懂的分享.

    把这套框架源码共享下  下载  

    下载说明:

      本框架没有用于过任何商业用途(当然以后就不一定了)

        欢迎 指正,批评,优化,建议,抄袭及商业使用

      共享的目的是为了优化一下框架,接收一下建议,了解下不足.

    转自https://www.cnblogs.com/xtdhb/p/3811635.html

  • 相关阅读:
    北风网第一季度菜单6
    北风网微信第一季菜单5
    win7卸载打印机驱动
    myeclipse 10激活,本人已测试过可行
    北风网视频菜单4
    Code Project精彩系列(1)
    Code Project精彩系列(1)
    Code Project精彩系列(1)
    实现Windows和Linux之间的文件共享
    实现Windows和Linux之间的文件共享
  • 原文地址:https://www.cnblogs.com/LiZhongZhongY/p/10864159.html
Copyright © 2011-2022 走看看