1. 感言
寫部落格之前先自我吹噓一下,給這些文章來些自我介紹。
半年前自己借用了5個多月的業務時間寫了一個個人ORM映射架構。在之前的博
客中也有過寫過該架構的相關介紹。半年前的那個ORM隻不過是自己想象的關系映射的一個雛形,那一段曾經讓自己驕傲過得代碼的确存在着太多的問題,但是我始終沒有放棄過對它的修改。又過了半年,這個ORM映射架構在之前的基礎上有了很大的改進。在此與大家分享一下,希望大家共同探讨,共同學習。
2. 架構整體介紹
說道這裡,其實這個ORM架構仍然存在這很大的問題。不過這個是自己的第二期目标,到現在這個程度算是完成了.
目前出現的ORM 架構還是比較多的。本人寫這個ORM不是為了說要推翻某個理論或者要和Linq,Nhibernate 一教高下,目前一個人之力幾乎不可能。寫此架構隻是為了更近一步的了解程式架構。
設計思路其實很簡單,和其餘的ORM架構一樣。通過某種關系來維持實體對象和資料庫之間的關系,然後通過實體對象的操作來實作資料庫的操作。
ORM是通過使用描述對象和資料庫之間映射的中繼資料,在我們想到描述的時候自然就想到了xml和特性(Attribute).目前的ORM架構中,Nhibernate 就是典型的使用xml檔案作為描述實體對象的映射架構,而大名鼎鼎的Linq 則是使用特性(Attribute) 來描述的。
我自己寫的這個出于學習特性這一知識,于是采用了特性(Attribute)來描述實體對象。
ORM 映射功能的實作組要由如下幾個組建組成:
(1)
實體—資料庫
映射特性關系
(2)
實體分析器
(3)
Sql 語句生成組建
(4)
資料庫操作庫
架構整體結構圖:
3. 實體—資料庫映射特性
對于資料庫和實體之間關系的描述,這個架構中使用了四個特性來描述。
TableAttribute 特性
字段名
作用
name
資料表名
該字段用于描述資料庫對應表的名稱,而且該值最好與
資料表名大小寫相同。該值有兩種類型。
(1)直接自定表的名稱
(2)[資料庫名].[表名]
如果是(2)情況,則需要分割字元串,将資料庫名分割
出來指派給dBName
dBName
資料庫名
該字段用于描述資料的名稱,而且該值最好與
資料庫名稱大小寫相同
primaryKeyName
主鍵字段名
該實體必須指定對應資料庫表的主鍵
information
表實體描述資訊
預設為""
isInternal
表實體是否國際化
預設為false
true 國際化 false 不國際化
version
表實體版本号
預設為
"V1.0"
特性源碼:
1 /**
2 * 2010-1-28
3 *
4 * 情 緣
5 *
6 * 實體表特性,用于描述實體和資料庫表之間的映射關系
7 *
8 * 該特性類使用 sealed 修改,說明該類不能夠再被繼承。
9 *
10 * */
11
12 using System;
13
14 namespace CommonData.Model.Core
15 {
16 public sealed class TableAttribute:Attribute
17 {
18 /// <summary>
19 /// 資料表名
20 /// 該字段用于描述資料庫對應表的名稱,而且該值最好與
21 /// 資料表名大小寫相同。該值有兩種類型。
22 /// (1)直接自定表的名稱
23 /// (2)[資料庫名].[表名]
24 /// 如果是(2)情況,則需要分割字元串,将資料庫名分割
25 /// 出來指派給dBName
26 /// </summary>
27 private string name;
28
29 /// <summary>
30 /// 資料庫名
31 /// 該字段用于描述資料的名稱,而且該值最好與
32 /// 資料庫名稱大小寫相同
33 /// </summary>
34 private string dBName;
35
36 /// <summary>
37 /// 主鍵字段名
38 /// 該實體必須指定對應資料庫表的主鍵
39 /// </summary>
40 private string primaryKeyName = "";
41
42 /// <summary>
43 /// 表實體描述資訊
44 /// 預設為""
45 /// </summary>
46 private string information = "";
47
48 /// <summary>
49 /// 表實體是否國際化
50 /// 預設為false
51 /// true 國際化 false 不國際化
52 /// </summary>
53 private bool isInternal = false;
54
55 /// <summary>
56 /// 表實體版本号
57 /// 預設為 "V1.0"
58 /// </summary>
59 private string version = "V1.0";
60
61
62 /// <summary>
63 /// 無參數構造方法
64 /// </summary>
65 public TableAttribute()
66 {
67
68 }
69
70 /// <summary>
71 /// 部分參數構造方法,構造改表特性的實體類
72 /// 并實作部分字段的初始化
73 /// </summary>
74 /// <param name="name">資料表名</param>
75 /// <param name="dBName">資料庫名</param>
76 /// <param name="primaryKeyName">主鍵字段名</param>
77 /// <param name="isInternal">表實體是否國際化</param>
78 public TableAttribute(string name,
79 string dBName,
80 string primaryKeyName,
81 bool isInternal)
82 {
83 this.name = name;
84 this.dBName = dBName;
85 this.primaryKeyName = primaryKeyName;
86 this.isInternal = isInternal;
87 }
88
89 /// <summary>
90 /// 全參數構造方法,構造改表特性的實體類
91 /// 并實作所有字段的初始化
92 /// </summary>
93 /// <param name="name">資料表名</param>
94 /// <param name="dBName">資料庫名</param>
95 /// <param name="primaryKeyName">主鍵字段名</param>
96 /// <param name="information">表實體描述資訊</param>
97 /// <param name="isInternal">表實體是否國際化</param>
98 /// <param name="version">表實體版本号</param>
99 public TableAttribute(string name,
100 string dBName,
101 string primaryKeyName,
102 string information,
103 bool isInternal,
104 string version)
105 {
106 this.name = name;
107 this.dBName = dBName;
108 this.primaryKeyName = primaryKeyName;
109 this.information = information;
110 this.isInternal = isInternal;
111 this.version = version;
112 }
113
114 /// <summary>
115 /// 資料表名
116 /// </summary>
117 public string Name
118 {
119 get { return name; }
120 set { name = value; }
121 }
122
123 /// <summary>
124 /// 資料庫名
125 /// </summary>
126 public string DBName
127 {
128 get { return dBName; }
129 set { dBName = value; }
130 }
131
132 /// <summary>
133 /// 主鍵字段名
134 /// </summary>
135 public string PrimaryKeyName
136 {
137 get { return primaryKeyName; }
138 set { primaryKeyName = value; }
139 }
140
141 /// <summary>
142 /// 表實體描述資訊
143 /// </summary>
144 public string Information
145 {
146 get { return information; }
147 set { information = value; }
148 }
149
150 /// <summary>
151 /// 表實體是否國際化
152 /// </summary>
153 public bool IsInternal
154 {
155 get { return isInternal; }
156 set { isInternal = value; }
157 }
158
159 /// <summary>
160 /// 表實體版本号
161 /// </summary>
162 public string Version
163 {
164 get { return version; }
165 set { version = value; }
166 }
167
168
169 }
170 }
171
特性說明:實體表特性,用于描述實體和資料庫表之間的映射關系,該特性類使用 sealed 修改,說明該類不能夠再被繼承。
ColumnAttribute特性
表字段名稱,該屬性的值最好與資料表的字段的名稱相同。
該字段的值有兩種格式:
(1) [表名].[字段名]
(2) [字段名]
如果該字段的值為(1)情況,則應分割字元串,将字段名
指派給name屬性,表名則指派給tableName
tableName
表字段對應表名稱
該值是可以為空的,如果name的值的情況滿足(1)情況,
可以分割的值指派給該屬性
dataType
表字段的資料類型
該屬性的類型為自定義類型,該字段是一個枚舉類型。
該字段描述了25中資料類型
length
表字段的長度
控制該字段對應的資料庫表字段值的最大長度
可以不指定該值
canNull
表字段是否可以為空
true 可以為空
false 不能為空
defaultValue
表字段的預設值
預設情況為null
isPrimaryKey
表字段是否為主鍵
true 為主鍵
false 不是外鍵
autoIncrement
表字段是否為自動增長列
true 是自動增長列
false 不是自動增長列
isUnique
确定某個字段是否唯一
true 是唯一的
false 不是唯一
regularExpress
表字段的比對規則
字段比對規則正規表達式
isForeignKey
表字段是否為外鍵
true 為外鍵
foreignTabName
表字段外鍵對應的表名稱
如果isForeignKey
為true,則需要指定其值
表字段的描述資訊
特性源碼:
1 /**
6 * 實體表特性,用于描述實體字段和資料庫表字段之間的映射關系
16 public sealed class ColumnAttribute:Attribute
19 /// 表字段名稱,該屬性的值最好與資料表的字段的名稱相同。
20 /// 該字段的值有兩種格式:
21 /// (1) [表名].[字段名]
22 /// (2) [字段名]
23 /// 如果該字段的值為(1)情況,則應分割字元串,将字段名
24 /// 指派給name屬性,表名則指派給tableName
25 /// </summary>
26 private string name;
27
28 /// <summary>
29 /// 表字段對應表名稱
30 /// 該值是可以為空的,如果name的值的情況滿足(1)情況,
31 /// 可以分割的值指派給該屬性
32 /// </summary>
33 private string tableName;
34
35 /// <summary>
36 /// 表字段的資料類型
37 /// 該屬性的類型為自定義類型,該字段是一個枚舉類型。
38 /// 該字段描述了25中資料類型
40 private DataType dataType;
43 /// 表字段的長度
44 /// 控制該字段對應的資料庫表字段值的最大長度
45 /// 可以不指定該值
46 /// </summary>
47 private int length;
48
49 /// <summary>
50 /// 表字段是否可以為空
51 /// true 可以為空
52 /// false 不能為空
53 /// </summary>
54 private bool canNull;
55
56 /// <summary>
57 /// 表字段的預設值
58 /// 預設情況為null
59 /// </summary>
60 private object defaultValue;
63 /// 表字段是否為主鍵
64 /// true 為主鍵
65 /// false 不是外鍵
66 /// </summary>
67 private bool isPrimaryKey = false;
68
69 /// <summary>
70 /// 表字段是否為自動增長列
71 /// true 是自動增長列
72 /// false 不是自動增長列
74 private bool autoIncrement = false;
75
76 /// <summary>
77 /// 确定某個字段是否唯一
78 /// true 是唯一的
79 /// false 不是唯一
80 /// </summary>
81 private bool isUnique;
82
83 /// <summary>
84 /// 表字段的比對規則
85 /// 字段比對規則正規表達式
86 /// </summary>
87 private string regularExpress;
90 /// 表字段是否為外鍵
91 /// true 為外鍵
92 /// false 不是外鍵
93 /// </summary>
94 private bool isForeignKey=false;
95
96 /// <summary>
97 /// 表字段外鍵對應的表名稱
98 /// 如果isForeignKey 為true,則需要指定其值
99 /// </summary>
100 private string foreignTabName;
101
102 /// <summary>
103 /// 表字段的描述資訊
104 /// </summary>
105 private string information;
106
107
108 /// <summary>
109 /// 無參數構造方法
110 /// </summary>
111 public ColumnAttribute()
112 {
113 }
114
115 /// <summary>
116 /// 部分參數構造方法,構造該特性執行個體時
117 /// 初始化部分實體屬性的值
118 /// </summary>
119 /// <param name="name">表字段名稱</param>
120 /// <param name="dataType">表字段的資料類型</param>
121 /// <param name="isPrimaryKey">表字段是否為主鍵</param>
122 /// <param name="autoIncrement">表字段是否為自動增長列</param>
123 /// <param name="isUnique">确定某個字段是否唯一</param>
124 /// <param name="isForeignKey">表字段是否為外鍵</param>
125 /// <param name="foreignTabName">表字段外鍵對應的表名稱</param>
126 public ColumnAttribute(string name,
127 DataType dataType,
128 bool isPrimaryKey,
129 bool autoIncrement,
130 bool isUnique,
131 bool isForeignKey,
132 string foreignTabName)
133 {
134 this.name = name;
135 this.dataType = dataType;
136 this.isPrimaryKey = isPrimaryKey;
137 this.autoIncrement = autoIncrement;
138 this.isUnique = isUnique;
139 this.isForeignKey = isForeignKey;
140 this.foreignTabName = foreignTabName;
141 }
142
143 /// <summary>
144 /// 全部參數構造方法,構造該特性執行個體時
145 /// 初始化全部實體屬性的值
146 /// </summary>
147 /// <param name="name">表字段名稱</param>
148 /// <param name="tableName">表字段對應表名稱</param>
149 /// <param name="dataType">表字段的資料類型</param>
150 /// <param name="length">表字段的長度</param>
151 /// <param name="canNull">表字段是否可以為空</param>
152 /// <param name="defaultValue">表字段的預設值</param>
153 /// <param name="isPrimaryKey">表字段是否為主鍵</param>
154 /// <param name="autoIncrement">表字段是否為自動增長列</param>
155 /// <param name="isUnique">确定某個字段是否唯一</param>
156 /// <param name="regularExpress">表字段的比對規則</param>
157 /// <param name="isForeignKey">表字段是否為外鍵</param>
158 /// <param name="foreignTabName">表字段外鍵對應的表名稱</param>
159 /// <param name="information">表字段的描述資訊</param>
160 public ColumnAttribute(string name,
161 string tableName,
162 DataType dataType,
163 int length,
164 bool canNull,
165 object defaultValue,
166 bool isPrimaryKey,
167 bool autoIncrement,
168 bool isUnique,
169 string regularExpress,
170 bool isForeignKey,
171 string foreignTabName,
172 string information)
173 {
174 this.name = name;
175 this.tableName = tableName;
176 this.dataType = dataType;
177 this.length = length;
178 this.canNull = canNull;
179 this.defaultValue = defaultValue;
180 this.isPrimaryKey = isPrimaryKey;
181 this.autoIncrement = autoIncrement;
182 this.regularExpress = regularExpress;
183 this.isForeignKey = isForeignKey;
184 this.ForeignTabName = foreignTabName;
185 this.information = information;
186 this.isUnique = isUnique;
187 }
188
189 /// <summary>
190 /// 表字段名稱
191 /// </summary>
192 public string Name
193 {
194 get { return name; }
195 set { name = value; }
196 }
197
198 /// <summary>
199 /// 表字段對應表名稱
200 /// </summary>
201 public string TableName
202 {
203 get { return tableName; }
204 set { tableName = value; }
205 }
206
207 /// <summary>
208 /// 表字段的資料類型
209 /// </summary>
210 public DataType DataType
211 {
212 get { return dataType; }
213 set { dataType = value; }
214 }
215
216 /// <summary>
217 /// 表字段的長度
218 /// </summary>
219 public int Length
220 {
221 get { return length; }
222 set { length = value; }
223 }
224
225 /// <summary>
226 /// 表字段是否可以為空
227 /// </summary>
228 public bool CanNull
229 {
230 get { return canNull; }
231 set { canNull = value; }
232 }
233
234 /// <summary>
235 /// 表字段的預設值
236 /// </summary>
237 public object DefaultValue
238 {
239 get { return defaultValue; }
240 set { defaultValue = value; }
241 }
242
243 /// <summary>
244 /// 表字段是否為主鍵
245 /// </summary>
246 public bool IsPrimaryKey
247 {
248 get { return isPrimaryKey; }
249 set { isPrimaryKey = value; }
250 }
251
252 /// <summary>
253 /// 表字段是否為自動增長列
254 /// </summary>
255 public bool AutoIncrement
256 {
257 get { return autoIncrement; }
258 set { autoIncrement = value; }
259 }
260
261 /// <summary>
262 /// 表字段的比對規則
263 /// </summary>
264 public string RegularExpress
265 {
266 get { return regularExpress; }
267 set { regularExpress = value; }
268 }
269
270 /// <summary>
271 /// 表字段是否為外鍵
272 /// </summary>
273 public bool IsForeignKey
274 {
275 get { return isForeignKey; }
276 set { isForeignKey = value; }
277 }
278
279 /// <summary>
280 /// 表字段外鍵對應的表名稱
281 /// </summary>
282 public string ForeignTabName
283 {
284 get { return foreignTabName; }
285 set { foreignTabName = value; }
286 }
287
288 /// <summary>
289 /// 表字段的描述資訊
290 /// </summary>
291 public string Information
292 {
293 get { return information; }
294 set { information = value; }
295 }
296
297 /// <summary>
298 /// 确定某個字段是否唯一
299 /// </summary>
300 public bool IsUnique
301 {
302 get { return isUnique; }
303 set { isUnique = value; }
304 }
305 }
306 }
307
特性說明:實體表特性,用于描述實體字段和資料庫表字段之間的映射關系,該特性類使用 sealed 修改,說明該類不能夠再被繼承。
LinkTableAttribute 特性
該實體屬性對應的資料庫表名
sqlPrefix
該實體屬性對應的資料庫表名字首
一般情況下沒有太大意思
該實體屬性對應的資料類型
可以通過發射獲得類型
keyName
該實體對應的主鍵類型
(用資料的列名來表示,不要使用實體的屬性來定義)
className
對應的實體的全路徑類型
isLazy
對應的實體是否延長加載
1 /**
6 * 實體屬性特性,用于描述某個實體作為另外一個實體
7 * 的一個屬性字段的時候與資料庫表之間的關系
8 *
9 * 這個實體特性類和其餘的幾個有差別,沒有用sealed
10 * 修飾,因為這個特性是用于修飾實體屬性的,而實體屬
11 * 性的集合也是一種特殊的屬性,是以可以使用
12 * LinkTablesAttribute 來繼承該特性。兩者在屬性描述
13 * 上基本沒有太大的差別
14 *
15 * */
16
17 using System;
18
19
20 namespace CommonData.Model.Core
21 {
22 public class LinkTableAttribute:Attribute
23 {
24 /// <summary>
25 /// 該實體屬性對應的資料庫表名
30 /// 該實體屬性對應的資料庫表名字首
31 /// 一般情況下沒有太大意思
33 private string sqlPrefix;
36 /// 該實體屬性對應的資料類型
37 /// 可以通過發射獲得類型
38 /// </summary>
39 private Type dataType;
40
41 /// <summary>
42 /// 該實體對應的主鍵類型
43 /// (用資料的列名來表示,不要使用實體的屬性來定義)
44 /// </summary>
45 private string keyName;
46
47 /// <summary>
48 /// 對應的實體的全路徑類型
49 /// </summary>
50 private string className;
51
52 /// <summary>
53 /// 對應的實體是否延長加載
54 /// </summary>
55 private bool isLazy;
56
57 /// <summary>
58 /// 無參數構造方法
60 public LinkTableAttribute()
61 {
62 }
63
64 /// <summary>
65 /// 部分參數構造方法,構造該特性執行個體的時候
66 /// 初始化部分屬性
67 /// </summary>
68 /// <param name="name">該實體屬性對應的資料庫表名</param>
69 /// <param name="dataType">該實體屬性對應的資料類型</param>
70 /// <param name="keyName">該實體對應的主鍵類型</param>
71 /// <param name="className">對應的實體的全路徑類型</param>
72 public LinkTableAttribute(string name, Type dataType, string keyName, string className)
73 {
74 this.name = name;
75 this.dataType = dataType;
76 this.keyName = keyName;
77 this.className = className;
78 }
79
80 /// <summary>
81 /// 全參數構造方法,構造該特性執行個體的時候
82 /// 初始化全部屬性
83 /// </summary>
84 /// <param name="name">該實體屬性對應的資料庫表名</param>
85 /// <param name="sqlPrefix">該實體屬性對應的資料庫表名字首</param>
86 /// <param name="dataType">該實體屬性對應的資料類型</param>
87 /// <param name="keyName">該實體對應的主鍵類型</param>
88 /// <param name="className">對應的實體的全路徑類型</param>
89 public LinkTableAttribute(string name,string sqlPrefix, Type dataType, string keyName, string className)
90 {
91 this.name = name;
92 this.sqlPrefix = sqlPrefix;
93 this.dataType = dataType;
94 this.keyName = keyName;
95 this.className = className;
96 }
97
98 public string Name
99 {
100 get { return name; }
101 set { name = value; }
102 }
103
104
105 public string SqlPrefix
106 {
107 get { return sqlPrefix; }
108 set { sqlPrefix = value; }
109 }
110
111
112 public Type DataType
113 {
114 get { return dataType; }
115 set { dataType = value; }
116 }
117
118
119 public string KeyName
120 {
121 get { return keyName; }
122 set { keyName = value; }
123 }
124
125
126 public string ClassName
128 get { return className; }
129 set { className = value; }
131
132 public bool IsLazy
134 get { return isLazy; }
135 set { isLazy = value; }
136 }
137 }
138 }
139
特性說明:實體屬性特性,用于描述某個實體作為另外一個實體的一個屬性字段的時候與資料庫表之間的關系.這個實體特性類和其餘的幾個有差別,沒有用sealed 修飾,因為這個特性是用于修飾實體屬性的,而實體屬 性的集合也是一種特殊的屬性,是以可以使用LinkTablesAttribute 來繼承該特性。兩者在屬性描述上基本沒有太大的差別
LinkTablesAttribute 特性
代碼
1 /**
2 * 2010-1-28
3 *
4 * 情 緣
5 *
6 * 實體集合屬性特性,用于描述某個實體作為另外一個實體
7 * 的一個集合屬性的時候與資料庫表之間的關系
8 *
9 * 這個特性使用了 sealed 修飾,說明該特性類不能再被繼
10 * 承。但是這個實體類不是在繼承Attribute,而是繼承
11 * LinkTableAttribute。因為這兩者描述的屬性具有相同的
12 * 特性。隻需要讓它繼承LinkTableAttribute 保留父類的
13 * 屬性就可以了。 LinkTablesAttribute,LinkTableAttribute
14 * 這兩個特性類具有相同的屬性,使用不同的類名隻是為了
15 * 區分修飾的屬性形态有所不同
16 *
17 * */
18
19 using System;
20
21 namespace CommonData.Model.Core
22 {
23 public sealed class LinkTablesAttribute:LinkTableAttribute
24 {
25 /// <summary>
26 /// 無參數構造方法
27 /// </summary>
28 public LinkTablesAttribute()
29 {
30 }
31
32 /// <summary>
33 /// 部分參數構造方法,構造該特性執行個體的時候,
34 /// 初始化部分屬性,并且調用的是父類的構造
35 /// 方法
36 /// </summary>
37 /// <param name="name">該實體屬性對應的資料庫表名</param>
38 /// <param name="dataType">該實體屬性對應的資料類型</param>
39 /// <param name="keyName">該實體對應的主鍵類型</param>
40 /// <param name="className">對應的實體的全路徑類型</param>
41 public LinkTablesAttribute(string name, Type dataType, string keyName, string className)
42 : base(name,dataType,keyName,className)
43 { }
44
45
46 /// <summary>
47 /// 全參數構造方法,構造該特性執行個體的時候
48 /// 初始化全部屬性 ,并且調用的是父類的構造
49 /// 方法
50 /// </summary>
51 /// <param name="name">該實體屬性對應的資料庫表名</param>
52 /// <param name="sqlPrefix">該實體屬性對應的資料庫表名字首</param>
53 /// <param name="dataType">該實體屬性對應的資料類型</param>
54 /// <param name="keyName">該實體對應的主鍵類型</param>
55 /// <param name="className">對應的實體的全路徑類型</param>
56 public LinkTablesAttribute(string name, string sqlPrefix, Type dataType, string keyName, string className)
57 :base(name,sqlPrefix,dataType,keyName,className)
58 {
59
60 }
61 }
62 }
63
特性說明:實體集合屬性特性,用于描述某個實體作為另外一個實體的一個集合屬性的時候與資料庫表之間的關系
這個特性使用了 sealed 修飾,說明該特性類不能再被繼承。但是這個實體類不是在繼承Attribute,而是繼承LinkTableAttribute。 因為這兩者描述的屬性具有相同的特性。隻需要讓它繼承LinkTableAttribute 保留父類的屬性就可以了。 LinkTablesAttribute,LinkTableAttribute這兩個特性類具有相同的屬性,使用不同的類名隻是為了區分修飾的屬性形态有所不同
(注: ORM涉及内容比較多,後續期待。本文隻是一個開端,有興趣的可以與本人探讨)