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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM