ORM,ORM的原理及測試案例


 

 

提綱

一、什么是ORM。
二、反射以及Attribute在ORM中的應用。
三、創建一個數據庫表和表對應的實體model。
四、實體model如何映射出數據庫表。
五、組合ORM映射生成insert語句。
六、測試ORM的插入映射。
七、總結。

內容:

一 、什么是ORM?

概念: 對象關系映射(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用於實現面向對象編程語言里不同類型系統的數據之間的轉換。

詳細介紹:  讓我們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關系"(Relational)。幾乎所有的程序里面,都存在對象和關系數據庫。在業務邏輯層和用戶界面層中,我們是面向對象的。當對象信息發生變化的時候,我們需要把對象的信息保存在關系數據庫中。 
        當你開發一個應用程序的時候(不使用O/R Mapping),你可能會寫不少數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息,等等。你在DAL中寫了很多的方法來讀取對象數據,改變狀態對象等等任務。而這些代碼寫起來總是重復的。 
        ORM解決的主要問題是對象關系的映射。域模型和關系模型分別是建立在概念模型的基礎上的。域模型是面向對象的,而關系模型是面向關系的。一般情況下,一個持久化類和一個表對應,類的每個實例對應表中的一條記錄,類的每個屬性對應表的每個字段。 
        ORM技術特點: 
        1.提高了開發效率。由於ORM可以自動對Entity對象與數據庫中的Table進行字段與屬性的映射,所以我們實際可能已經不需要一個專用的、龐大的數據訪問層。 
        2.ORM提供了對數據庫的映射,不用sql直接編碼,能夠像操作對象一樣從數據庫獲取數據。

 

二、反射以及Attribute在ORM中的應用。

什么是反射?
簡單點吧,反射就是在運行時動態獲取對象信息的方法,比如運行時知道對象有哪些屬性,方法,委托等等等等。
反射有什么用呢?
反射不但讓你在運行是獲取對象的信息,還提供運行時動態調用對象方法以及動態設置、獲取屬性等的能力。
反射在ORM中有什么用呢?
我 這里所討論的ORM實現是通過自定義Attribute的方式進行映射規則的描述的。但是我們並不知道具體哪個對象需要對應哪個表,並且這些對象是獨立於 我們的ORM框架的,所以我們只能通過自定義Attribute來定義映射規則,然后通過反射來動態獲取這些映射規則。
(這里只簡單說明下概念:具體如何實現過程請看第四項。)

三、創建一個數據庫表和表對應的實體model。

     傳統的創建表和model實體的創建過程。


    1.創建數據庫表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create  table  TB_People
(
      Pl_ID  Int  identity(1,1)  primary  key  ,      
 
     PL_Age  Int ,
 
     Pl_Sex Nvarchar(4),
 
      Pl_LoginName nvarchar(30),
 
      Pl_TrueName  nvarchar(30),
 
      PL_Pwd  nvarchar(60)
)

 
  2.根據表結構一般我們會創建如下model實體

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
58
59
60
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
 
namespace  FuzhuKeji
{
     public  class  M_People
     {
 
         string  _Pl_ID;
 
         public  string  Pl_ID
         {
             get  return  _Pl_ID; }
             set  { _Pl_ID = value; }
         }
 
         int  _PL_Age;
 
         public  int  PL_Age
         {
             get  return  _PL_Age; }
             set  { _PL_Age = value; }
         }
 
         string  _Pl_Sex;
 
         public  string  Pl_Sex
         {
             get  return  _Pl_Sex; }
             set  { _Pl_Sex = value; }
         }
 
         string  _Pl_LoginName;
 
         public  string  Pl_LoginName
         {
             get  return  _Pl_LoginName; }
             set  { _Pl_LoginName = value; }
         }
 
         string  _Pl_TrueName;
 
         public  string  Pl_TrueName
         {
             get  return  _Pl_TrueName; }
             set  { _Pl_TrueName = value; }
         }
 
         string  _PL_Pwd;
 
         public  string  PL_Pwd
         {
             get  return  _PL_Pwd; }
             set  { _PL_Pwd = value; }
         }
 
     }
}

 

 

現在看到了表結構 和model實體,那如何根據model實體映射出表的插入語句及結構呢?下面我們就介紹有model映射到數據庫表。

 

四、實體model如何映射出數據庫表。

 

上面簡單介紹了反射以及Attribute在ORM中的應用,那如何通過這些進行映射出來的呢?

方法一:

 

看到 題綱三中的model實體了,下面我們就通過反射的方法來動態獲取此映射規則:

 

復制代碼
/// <summary>
        ///  測試映射
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            M_People mp = new M_People();
            mp.PL_Age = 26;
            mp.Pl_ID = "001";
            mp.Pl_LoginName = "Test1";
            mp.PL_Pwd = "123";
            mp.Pl_Sex = "男";
            mp.Pl_TrueName = "張三";
            PropertyInfo[] infos = mp.GetType().GetProperties();

            string Message_shuxing1 = "";

            foreach (PropertyInfo info in infos)
            {
                //獲取屬性並打印
                Message_shuxing1 = Message_shuxing1 + (info.Name + ":" + info.GetValue(mp, null));
            }

            MessageBox.Show("這里看到可以獲得屬性名稱和屬性值(是不是對ORM有點慢慢明白了):"+Message_shuxing1);

            // 上面info.GetValue(mp, null)獲得屬性的值。
            //info.SetValue(mp, "XX", null);  賦值

        }
復制代碼

 

測試效果圖如下:

 

 

是不是有點思路了,知道如何搞了,呵呵。

 

看到紅色部分了嗎

 

 

 

有感覺了沒有:是不是和數據庫的名稱一樣,而且還獲得了值。為什么會出現這種情況呢?

 

 

 

屬性是來至那?--Model實體吧,屬性的名稱也是model實體屬性名稱吧。所以我們只要把屬性的名稱按某個規則定義就可以獲得其對應的數據庫字段名和類型。

方法二:

 

備注下:其實不只這種方法可以完成ORM的映射,而且還可以通過Attribute:
Attribute中文翻譯雖然也號稱“屬性”,但是她和對象的屬性(Property)其實是完全不同的兩概念。她是在運行時對對象或者對象屬性、方法、委托等等進行描述的類,用於在運行時描述你的代碼或者在運行時影響你的程序的行為。
其 實我們在c#的編程中經常看到Attribute,只不過我們沒有注意罷了。比如Main函數前的“[STAThread]”這個其實就是一個 Attribute。全程為[STAThreadAttribute]。另外指定類可序列化的[Serializable]等等。是不是都很熟悉啊?只不 過平時估計沒有用到,所以沒有注意罷了。

 

既然Attribute是類,那么她的定義方法和類就沒有兩樣了,唯一的不同就是自定義Attribute類必須繼承於System.Attribute。

 

那我們改下M_People實體的東西如下:

 

下面我們來簡單定義一個描述數據庫字段信息的Attribute,在此類中我們采用更省略的方式,僅僅提供“字段名”,“字段類型”:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  class  DataFieldAttribute : Attribute
     {
         private  string  _FieldName;
         private  string  _FieldType;
         public  DataFieldAttribute( string  fieldname,  string  fieldtype)
         {
             this ._FieldName = fieldname;
             this ._FieldType = fieldtype;
         }
         public  string  FieldName
         {
             get  return  this ._FieldName; }
             set  this ._FieldName = value; }
         }
         public  string  FieldType
         {
             get  return  this ._FieldType; }
             set  this ._FieldType = value; }
         }
     }

 

 

那我們把Mode更改下改為如下:

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
public  class  M_People
     {
 
         string  _Pl_ID;
         [DataFieldAttribute( "Pl_ID" "Int" )]
         public  string  Pl_ID
         {
             get  return  _Pl_ID; }
             set  { _Pl_ID = value; }
         }
 
         int  _PL_Age;
         [DataFieldAttribute( "PL_Age" "Int" )]
         public  int  PL_Age
         {
             get  return  _PL_Age; }
             set  { _PL_Age = value; }
         }
 
         string  _Pl_Sex;
         [DataFieldAttribute( "Pl_Sex" "nvarchar" )]
         public  string  Pl_Sex
         {
             get  return  _Pl_Sex; }
             set  { _Pl_Sex = value; }
         }
 
         string  _Pl_LoginName;
         [DataFieldAttribute( "Pl_LoginName" "nvarchar" )]
         public  string  Pl_LoginName
         {
             get  return  _Pl_LoginName; }
             set  { _Pl_LoginName = value; }
         }
 
         string  _Pl_TrueName;
         [DataFieldAttribute( "Pl_TrueName" "nvarchar" )]
         public  string  Pl_TrueName
         {
             get  return  _Pl_TrueName; }
             set  { _Pl_TrueName = value; }
         }
 
         string  _PL_Pwd;
         [DataFieldAttribute( "PL_Pwd" "nvarchar" )]
         public  string  PL_Pwd
         {
             get  return  _PL_Pwd; }
             set  { _PL_Pwd = value; }
         }
 
     }

通過自定義Attribute,我們定義了類屬性和數據庫字段的一一對應關系。

那我們通過事件測試下方法案例:

 

復制代碼
/// <summary>
        /// 反射+Attribute 映射出數據庫表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            M_People mp = new M_People();
            mp.PL_Age = 26;
            mp.Pl_ID = "001";
            mp.Pl_LoginName = "Test1";
            mp.PL_Pwd = "123";
            mp.Pl_Sex = "男";
            mp.Pl_TrueName = "張三";

            PropertyInfo[] infos = mp.GetType().GetProperties();
            string Str_TestAtrrubute = "";

            object[] objDataFieldAttribute = null;
            foreach (PropertyInfo info in infos)
            {
                objDataFieldAttribute = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
                if (objDataFieldAttribute != null)
                {
                  Str_TestAtrrubute=Str_TestAtrrubute+(info.Name + "->數據庫字段:" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName)+" --------";
                }
            }

            MessageBox.Show(Str_TestAtrrubute);
        }
復制代碼

 

 

 

測試的效果圖如下:

 

 

哈 哈,你是不是很想動手了啊?

 

加油!下面我們就介紹如何實現插入語句的映射!

 

五、組合ORM映射生成insert語句。

    我們仔細思考下看到上面的我們會怎么想才可以生成一條sql語句並且執行。

     首先我們是不是應該分開兩部分,

        第一步負責生成插入語句。

        第二步是負責執行插入語句得。

  

      我們繼續思考?生成一條插入語句我們要考慮哪些問題?

         a、是不是返回值

         b、是不是要判斷表中是否有不需要組合為插入語句的字段(如自增字段)

         c、而且這個插入語句是針對對象的插入語句而不是固定的某個或者已知的某個實體。所以我們會考慮到泛型的使用。

          這樣我們基本確定了insert語句的參數和結構。

        我們再回到第一步如何根據實體生成插入語句? 我們第四部也只是說了根據實體映射出和數據庫字段一樣的名字,這有什么用呢?

        肯定根據這些字段名我們要想辦法組合個sql語句。繼續分析、、、

         如何分工 :肯定要給個執行的sql語句

          分工一:是不是獲得屬性名稱組合sql。

          分工二:是不是做個參數的對應表。

          分工三:組合這些東西,並把其屬性類型和數據庫字段類型對應起來。

 

上面說了那么多,只是幫大家打開思路,其實只要你理解了映射(第四項),用自己的思路去寫那些組合sql也可以得,

我這個地方寫的也不見得完美,只是給大家做個例子,嘿嘿,一起加油!

 

有幾個地方用到了枚舉首先我列出枚舉的方法:

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
第一個屬性標識是否為主鍵或者讀寫的標識
[Serializable]
   [Flags]
   public  enum  ColumnKeyType
   {
       /// <summary>
       /// 默認狀態
       /// </summary>
       Default = 1,
 
       /// <summary>
       /// 標識為主鍵
       /// </summary>
       Identity = 2,
 
       /// <summary>
       /// Extend狀態下,不參與讀取、增加、修改
       /// </summary>
       Extend = 4,
 
       /// <summary>
       /// Read狀態下不參與增加、修改
       /// </summary>
       Read = 8
   }
 
返回值做了枚舉:
   public  enum  DBReturnType
   /// <summary>
       /// 返回受影響的行數
       /// </summary>
       EffectRow,
       /// <summary>
       /// 返回最后插入的主鍵值
       /// </summary>
       Identity
   }

 

 

插入語句的代碼:

 

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#region 把對象內容保存到數據庫中 Insert
       /// <summary>
       /// 把對象內容保存到數據庫中
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <param name="model"></param>
       /// <param name="isIncludeKeyColumn">插入語句中是否包含對主鍵的插入,當主鍵值為自動增加時為false</param>
       /// <param name="returnType">返回的數據類型:DBReturnType.EffectRow 為返回受影響行數;DBReturnType.IdEntity 返回最新插入主鍵值(isIncludeKeyColumn == false時有效)</param>
       public  static  int  Insert<T>(T model,  bool  isIncludeKeyColumn, DBReturnType returnType)  where  T :  class
       {
           int  i = 0;
           Type type =  typeof (T);
 
           //獲取表名
           string  tableName = EntityHelper.GetTableName(type);
 
           PropertyInfo[] pis = type.GetProperties();
 
           //獲取所有字段和主鍵名稱
           List< string > columns =  null ;
 
           //處理是否包含主鍵插入
           if  (isIncludeKeyColumn ==  false )
           {
               columns = EntityHelper.GetTableColumns(pis, ColumnKeyType.Identity | ColumnKeyType.Extend,  null );
           }
           else
           {
               columns = EntityHelper.GetTableColumns(pis, ColumnKeyType.Extend,  null );
           }
 
           //生成INSERT語句
           StringBuilder sqlText =  new  StringBuilder();
           sqlText.Append( "INSERT INTO " );
           sqlText.Append(tableName);
           sqlText.Append( " (" );
 
           //第一個字段
           sqlText.Append(columns[0]);
 
           //第二個起所有字段
           int  loop = columns.Count;
           for  (i = 1; i < loop; i++)
           {
               sqlText.Append( "," );
               sqlText.Append(columns[i]);
           }
 
           sqlText.Append( ") VALUES (" );
 
           //第一個字段
           sqlText.Append( "@" );
           sqlText.Append(columns[0]);
 
           //第二個起所有字段
           for  (i = 1; i < loop; i++)
           {
               sqlText.Append( ",@" );
               sqlText.Append(columns[i]);
           }
 
           sqlText.Append( ");" );
 
           //生成MySqlParamter
           PropertyInfo propertyInfo =  null ;
 
           SqlParameter[] paras =  new  SqlParameter[loop];
           for  (i = 0; i < loop; i++)
           {
               propertyInfo = type.GetProperty(columns[i]);
               paras[i] =  new  SqlParameter(columns[i], GetMySqlDbType(propertyInfo.PropertyType), -1);
               paras[i].Value = propertyInfo.GetValue(model,  null );
           }
 
           //根據兩種情況返回不同的值
           if  (isIncludeKeyColumn ==  false  && returnType == DBReturnType.Identity)
           {
               sqlText.Append( " SELECT @@identity AS RetId" );
               SqlDataReader sdr = DataReader(sqlText.ToString(), CommandType.Text, paras);
               int  keyId = 0;
               if  (sdr.Read())
               {
                   keyId = Convert.ToInt32(sdr[ "RetId" ]);
               }
               sdr.Close();
 
               return  keyId;
           }
           else
           {
               return  NonQuery(sqlText.ToString(), CommandType.Text, paras);
           }
       }
 
 
 
 
       #endregion
 
#region 根據Type類型獲取SQL的數據類型
 
       /// <summary>
       /// 根據Type類型獲取MySQL的數據類型
       /// </summary>
       /// <param name="type"></param>
       /// <returns></returns>
       private  static  SqlDbType GetMySqlDbType(Type type)
       {
           SqlDbType dbtype = SqlDbType.VarChar;
 
           if  (type.Equals( typeof ( string )))
           {
 
           }
           else  if  (type.Equals( typeof ( int )))
           {
               dbtype = SqlDbType.Int;
           }
           else  if  (type.Equals( typeof ( bool )))
           {
               dbtype = SqlDbType.Bit;
           }
           else  if  (type.Equals( typeof (DateTime)))
           {
               dbtype = SqlDbType.DateTime;
           }
           else  if  (type.Equals( typeof ( decimal )))
           {
               dbtype = SqlDbType.Decimal;
           }
           else  if  (type.Equals( typeof ( float )))
           {
               dbtype = SqlDbType.Float;
           }
           else  if  (type.Equals( typeof ( double )))
           {
               dbtype = SqlDbType.Float;
           }
 
           return  dbtype;
       }
 
      #endregion

 

下面我們簡單定義一個描述數據庫字段信息的Attribute 包括表名 屬性字段獲得

從Model模型中獲取數據表名、主鍵名、 獲取需要的讀取數據源的字段集

(忘了說明表的表名寫在那個地方,其實表名只需要定義在類的上面就可以了)為了簡單起見我還是把目前的model放到代碼里面

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
58
59
60
61
62
63
64
65
66
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
 
namespace  FuzhuKeji
{
     [Serializable]
     [Property( "TB_People" )]
     public  class  M_People
     {
 
         string  _Pl_ID;
 
         /// <summary>
         /// 主鍵
         /// </summary>
         [Property(ColumnKeyType.Identity)]
         public  string  Pl_ID
         {
             get  return  _Pl_ID; }
             set  { _Pl_ID = value; }
         }
 
         int  _PL_Age;
        
         public  int  PL_Age
         {
             get  return  _PL_Age; }
             set  { _PL_Age = value; }
         }
 
         string  _Pl_Sex;
     
         public  string  Pl_Sex
         {
             get  return  _Pl_Sex; }
             set  { _Pl_Sex = value; }
         }
 
         string  _Pl_LoginName;
         
         public  string  Pl_LoginName
         {
             get  return  _Pl_LoginName; }
             set  { _Pl_LoginName = value; }
         }
 
         string  _Pl_TrueName;
      
         public  string  Pl_TrueName
         {
             get  return  _Pl_TrueName; }
             set  { _Pl_TrueName = value; }
         }
 
         string  _PL_Pwd;
    
         public  string  PL_Pwd
         {
             get  return  _PL_Pwd; }
             set  { _PL_Pwd = value; }
         }
 
     }
}

 

好吧這樣就不用擔心了映射表名獲得字段的代碼在下面:

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#region 下面我們簡單定義一個描述數據庫字段信息的Attribute 包括表名 屬性字段獲得
   public  class  PropertyAttribute : Attribute
   {
       public  string  tableName;
       public  ColumnKeyType columnKeyType;
 
       /// <summary>
       /// 重構方法默認值
       /// </summary>
       public  PropertyAttribute()
       {
           this .columnKeyType = ColumnKeyType.Default;
       }
 
       /// <summary>
       ///
       /// </summary>
       /// <param name="tableName"></param>
       public  PropertyAttribute( string  tableName)
       {
           this .tableName = tableName;
       }
 
       public  PropertyAttribute(ColumnKeyType columnKeyType)
       {
           this .columnKeyType = columnKeyType;
       }
   }
 
   #endregion
 
   #region  從Model模型中獲取數據表名、主鍵名、 獲取需要的讀取數據源的字段集
 
   public  class  EntityHelper
   {
       /// <summary>
       /// 從Model模型中獲取數據表名
       /// </summary>
       public  static  string  GetTableName(Type type)
       {
           PropertyAttribute property = (PropertyAttribute)(type.GetCustomAttributes( false )[0]);
           return  property.tableName;
       }
 
 
 
       /// <summary>
       /// 從Model模型中獲取數據主鍵名
       /// </summary>
       public  static  PropertyInfo GetTableIdentity(PropertyInfo[] pis)
       {
           object [] infos =  null ;
           PropertyAttribute attribute =  null ;
           foreach  (PropertyInfo pi  in  pis)
           {
               infos = pi.GetCustomAttributes( false );
               if  (infos.Length > 0)
               {
                   attribute = (PropertyAttribute)(infos[0]);
                   if  (attribute.columnKeyType == ColumnKeyType.Identity)
                   {
                       return  pi;
                   }
               }
           }
 
           return  null ;
       }
 
       /// <summary>
       /// 獲取需要的讀取數據源的字段集
       /// </summary>
       /// <param name="pis">Model模型所有屬性集合</param>
       /// <param name="filter"></param>
       /// <param name="customColumns">自定義查詢列名集合,使用逗號分隔。如不需要則為null</param>
       /// <returns></returns>
       public  static  List< string > GetTableColumns(PropertyInfo[] pis, ColumnKeyType filter,  string  customColumns)
       {
           string  col =  "" ;
           return  GetTableColumns(pis, filter, customColumns,  ref  col);
       }
 
 
       /// <summary>
       /// 獲取需要的讀取數據源的字段集
       /// </summary>
       /// <param name="pis">Model模型所有屬性集合</param>
       /// <param name="filter"></param>
       /// <param name="customColumns">自定義查詢列名集合,使用逗號分隔。如不需要則為null</param>
       /// <returns></returns>
       public  static  List< string > GetTableColumns(PropertyInfo[] pis, ColumnKeyType filter,  string  customColumns,  ref  string  outCol)
       {
           List< string > columns =  new  List< string >();
           if  (customColumns !=  null  && customColumns.Length > 0)
           {
               /*
                * 需要安全處理
                * 限制字段不包含空格
                */
               customColumns = customColumns.Trim();
               string [] strs = customColumns.Split( ',' );
               foreach  ( string  str  in  strs)
               {
                   if  (IsRegexMatch(str,  @"^(\w[^\s';]+)$" ))
                   {
                       columns.Add(str);
                   }
               }
 
               outCol = customColumns;
           }
           else
           {
               object [] infos =  null ;
               PropertyAttribute attribute =  null ;
               foreach  (PropertyInfo pi  in  pis)
               {
                   //刪除外部擴展對象項
                   infos = pi.GetCustomAttributes( false );
                   if  (infos.Length > 0)
                   {
                       attribute = (PropertyAttribute)(infos[0]);
                       if  (attribute.columnKeyType == (filter & attribute.columnKeyType))
                       {
                           continue ;
                       }
                   }
                   outCol +=  string .Concat( "," , pi.Name);
                   columns.Add(pi.Name);
               }
 
               outCol = outCol.Remove(0, 1);
           }
 
           return  columns;
       }
 
     
       /// <summary>
       /// 檢查是否滿足某種正則表達式
       /// </summary>
       private  static  bool  IsRegexMatch( string  str,  string  Express)
       {
           if  ( string .IsNullOrEmpty(str))
           {
               return  false ;
           }
 
           return  Regex.IsMatch(str, Express);
 
       }
   }
 
   #endregion

 上面就完成了sql語句的生成:下面我就定義一個sql語句的執行就可以了。其實你到insert方法里會發現 在生產完sql語句就調用執行方法了。

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
58
59
60
61
62
63
64
/// <summary>
/// 配置字符串參數
/// </summary>
private  static  void  PrepareCommand(SqlConnection conn, SqlTransaction trans, SqlCommand sqlCommand,  string  sqlText, CommandType commandType, SqlParameter[] parms)
{
     if  (conn.State != ConnectionState.Open)
     {
         conn.Open();
     }
 
     sqlCommand.Connection = conn;
     sqlCommand.CommandText = sqlText;
     sqlCommand.CommandType = commandType;
 
     if  (trans !=  null )
     {
         sqlCommand.Transaction = trans;
     }
 
     if  (parms !=  null )
     {
         foreach  (SqlParameter parm  in  parms)
         {
             sqlCommand.Parameters.Add(parm);
         }
     }
}
 
 
/// <summary>
/// 執行SQL語句,返回數據集
/// </summary>
public  static  SqlDataReader DataReader( string  sqlText, CommandType commandType, SqlParameter[] parms)
{
     SqlConnection conn =  new  SqlConnection( @"Data Source=HAOFUQI\SQLEXPRESS;Initial Catalog=Fukusuke;Persist Security Info=True;User ID=sa;pwd=123" );
     SqlCommand sqlCommand =  new  SqlCommand();
     PrepareCommand(conn,  null , sqlCommand, sqlText, commandType, parms);
 
     SqlDataReader reader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
 
     sqlCommand.Dispose();
     return  reader;
}
 
 
/// <summary>
/// 執行SQL語句,並返回影響行數
/// </summary>
public  static  int  NonQuery( string  sqlText, CommandType commandType, SqlParameter[] parms)
{
     int  reVal = 0;
     using  (SqlConnection conn =  new  SqlConnection( @"Data Source=HAOFUQI\SQLEXPRESS;Initial Catalog=Fukusuke;Persist Security Info=True;User ID=sa;pwd=123" ))
     {
         SqlCommand sqlCommand =  new  SqlCommand();
         PrepareCommand(conn,  null , sqlCommand, sqlText, commandType, parms);
 
         reVal = sqlCommand.ExecuteNonQuery();
      
         sqlCommand.Parameters.Clear();
         sqlCommand.Dispose();
     }
 
     return  reVal;
}

 

 

六、測試ORM的插入映射。

復制代碼
   /// <summary>
        ///  執行插入語句
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            M_People mp = new M_People();
            mp.PL_Age = 26;
            mp.Pl_ID = "001";
            mp.Pl_LoginName = "Test1";
            mp.PL_Pwd = "123";
            mp.Pl_Sex = "男";
            mp.Pl_TrueName = "張三";
           int Insert_Key=  DBHelper.Insert<M_People>(mp,false ,DBReturnType.Identity);
           MessageBox.Show("添加成功! 插入數據的主鍵為:"+Insert_Key.ToString());
        }
復制代碼

測試結果如下:

 

數據庫插入效果:

 

這個地方就插入語句成功了。

 

 

七、總結。

 

這篇文章寫得不是很好,我只是想描述,卻沒有一層層去剝開,也許這篇文章太長了吧。
這篇文章的核心應該是說明白如何映射如何到sql。沒有分層的概念。看起來混亂,但是如果分層我就要更多篇幅講解。

寫的錯誤或者不好的地方還請多多批評指導。

 

上一篇寫的是:為初學者寫三層.為初學者寫三層,三層的搭建和測試例子

下面准備寫ORM框架,然后把生成器集成到框架中。(偽三層+ORM)+生成器+常用類庫 用一個財務報銷系統作為案例。

好了就寫到這吧!

 

本文主要參考文獻:

1. 什么是ORM :http://www.cnblogs.com/double1030/archive/2009/02/01/1382062.html

2.ORM硬傷 :http://www.cnblogs.com/Barton131420/archive/2007/01/07/613955.html

3反射以及Attribute在ORM中的應用:.http://blog.csdn.net/ronotian/article/details/2900714

 

下載地址:http://pan.baidu.com/s/1bn6bM3d


免責聲明!

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



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