構造我本身的ORM

GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.com
來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=495php

經過前面兩章的描述,我相信不少朋友都已經明白我了下面將要討論到的ORM的實現方法了,那就是根據自定義Attribute來定義O/R Mapping規則,而後經過反射來動態獲取此規則,動態構造SQL語句。
因爲這個小東西(ORM)出生在深圳,因此我想來想去,她應該有個深圳的名字,因此我就叫她「MiniORM」。不知道各位認爲如何?
MiniORM採用的是ONE_INHERIT_TREE_ONE_CLASS(一個繼承樹對應於一個表)的結構,雖然這種結構容易致使數據冗餘,可是這種結構很簡單。另,本MiniORM 僅僅考慮一個表一個PK,一個FK的狀況。
MiniORM結構以下,爲了更便於理解和使用,我使用了3個類:
一、OrmWriter:負責將實體對象(好比前面章節說的Person)插入數據庫和修改數據庫中對應的記錄。
二、OrmRemover:負責根據實體對象,刪除指定的記錄;
三、OrmReader:負責根據實體對象,讀取指定的記錄;
構造我本身的ORM
上面就是MiniORM的3個主要類。下面咱們就詳細地根據前面的描述一步步構造她。咱們這裏仍是之前面說的Person爲例進行說明。
經過本系列第一章,咱們知道,對象不但存在繼承關係,特別在實際的應用中還存在包含關係,好比一個Person包含兩個Hand(手)類,包含一個Head(頭)類等,咱們的Person在數據庫中應該有一個ID,爲了更加方便使用和討論,此ID在MiniORM中是一個int以及自動增加類型(ID INDENTITY(1,1))。這些都是咱們的MiniORM應該考慮的範圍。
咱們對咱們的Person作修改:數據庫

  1. [DataObjectAttribute("Person")]
  2. public class Person
  3. {
  4. private int _ID;
  5. private string _Name;
  6. private int _Age;
  7. private string _Sex;
  8. private Head _Head;
  9. private Hand _LeftHand;
  10. private Hand _RightHand;
  11. public int ID
  12. {
  13. get { return _ID; }
  14. set { _ID = value; }
  15. }
  16. public Head Head
  17. {
  18. get { return _Head; }
  19. set { _Head = value; }
  20. }
  21. public Hand LeftHand
  22. {
  23. get { return _LeftHand; }
  24. set { _LeftHand = value; }
  25. }
  26. public Hand RightHand
  27. {
  28. get { return _RightHand; }
  29. set { _RightHand = value; }
  30. }
  31. [DataFieldAttribute("name", "NvarChar")]
  32. public string Name
  33. {
  34. get { return this._Name; }
  35. set { this._Name = value; }
  36. }
  37. [DataFieldAttribute("age", "int")]
  38. public int Age
  39. {
  40. get { return this._Age; }
  41. set { this._Age = value; }
  42. }
  43. [DataFieldAttribute("sex", "NvarChar")]
  44. public string Sex
  45. {
  46. get { return this._Sex; }
  47. set { this._Sex = value; }
  48. }
  49. }
    你可能又發現了一個問題,就是在咱們修改後的Person中,增長了LeftHand,RightHand以及Head,但是這三個都屬於類啊,這個怎麼可以保存到數據庫中呢?而且使用咱們前面的DataFieldAttribute是沒有辦法描述的啊。另外還增長了個ID,又怎麼來標誌這個是自動增加的int型PK呢?固然了可以到這裏你就發現這些問題那是至關的不錯了。若是前面就動手的人,估計考慮的仍是修改咱們的DataFieldAttribute讓它可以對這些東西進行區別。好比在DataFieldAttribute中再增長一個屬性用於區別哪一個是ID屬性,哪一個是對象類型(好比Hand)屬性。這固然是好的,只不過這樣作致使咱們的代碼極其醜陋。最好的辦法仍是另外增長一個Attribute。固然了,我是爲了更加方便的構造SQL語句,我作的不是很好。

一、怎麼表示實體類對應的數據庫表的PK和FK?
爲了更方便的實現,MiniORM中標誌一個實體類的PK和FK都是在DataObjectAttribute中來作(其實最好的辦法仍是另外增長個好比PKAttribute和FKAttribute,不過這個留給其它人去作吧)。以下,DataObjectAttribute第一個參數表示對應的數據庫表,第二個參數表示PK,第三個參數表示FK:服務器

  1. [DataObjectAttribute("Person", "ID", "")]
  2. public class Person
  3. {
  4. ......
  5. }
    二、怎麼標誌字段是Indentity(自動增加)?
    在DataFieldAttribute中增長了個屬性,用於標誌某個字段是否自動增加的字段。這些都是我我的懶作的,其中,第二個參數標誌ID是Identity類型
  6. [DataFieldAttribute("ID", true)]
  7. public int ID
  8. {
  9. get { return _ID; }
  10. set { _ID = value; }
  11. }
    三、怎樣標誌字段是類對象(好比Person中的Hand,固然複雜點的對象,可能包含子對象列表)?
    因爲MiniORM提供的是一個相似框架的東西,因此不該該受到實體類的限制,因此對於類對象字段,咱們應該可以描述此對象所在的程序集,命名空間,類名,這樣咱們才能夠運行時建立該對象。
  12. public class SubDataObjectAttribute : Attribute
  13. {
  14. private SubDataObjectFieldType _FieldType;
  15. private string _AssemblyName;
  16. private string _NamespaceName;
  17. private string _ClassName;
  18. public SubDataObjectAttribute(SubDataObjectFieldType fieldtype, string assemblyname, string namespacename, string classname)
  19. {
  20. this._FieldType = fieldtype;
  21. this._AssemblyName = assemblyname;
  22. this._NamespaceName = namespacename;
  23. this._ClassName = classname;
  24. }
  25. /// <summary>
  26. /// 本記錄對應的FieldType
  27. /// </summary>
  28. public SubDataObjectFieldType FieldType
  29. {
  30. get { return _FieldType; }
  31. }
  32. /// <summary>
  33. /// 本記錄對應的AssemblyName
  34. /// </summary>
  35. public string AssemblyName
  36. {
  37. get { return _AssemblyName; }
  38. }
  39. /// <summary>
  40. /// 本記錄對應的NamespaceName
  41. /// </summary>
  42. public string NamespaceName
  43. {
  44. get { return _NamespaceName; }
  45. }
  46. /// <summary>
  47. /// 本記錄對應的ClassName
  48. /// </summary>
  49. public string ClassName
  50. {
  51. get { return _ClassName; }
  52. }
  53. }
    其中SubDataObjectFieldType是一個枚舉類型,由於咱們的子對象多是單獨的對象好比Person.Head也多是一個列表(List)。因此我增長了這個枚舉類型,用於作標誌。
  54. public enum SubDataObjectFieldType
  55. {
  56. Object,
  57. /// <summary>
  58. /// 本字段屬於List類型,直接遍歷
  59. /// </summary>
  60. List,
  61. }
    固然了,這裏的子對象列表多是ArrayList,HashTable等等,你均可以根據本身項目中實際使用到的類型來作相應的修改。
    四、怎麼控制某個字段在表中不能重複?
    好比咱們要控制Person.Name不能重複,若是你新增的時候發現重複要提示。那咱們也經過增長一個Attribute的形式來實現。這個Attribute很簡單,沒有任何方法和屬性。
  62. public class DataFieldNotDoubleAttribute : Attribute
  63. {
  64. }
    五、怎樣作事務處理?
    事務處理是每一個底層框架都應該考慮到的問題,在.NET中咱們有兩種方式來進行事務處理,一種是使用COM+,這是最好的方法,不過性能上比較欠缺,另外這東西配置很麻煩,當你數據庫安裝在另一太服務器上的時候,每每出現沒法使用的問題,我曾經就被這東西折騰夠嗆,因此我乾脆就不用他了,不過仍是介紹下語法,經過使用TransactionScope就能夠很好的使用com+提供的事務處理,代碼至關的簡潔,優美,只惋惜啊!天使的面孔,魔鬼的心。
  65. public void function1()
  66. {
  67. using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
  68. {
  69. function2();
  70. }
  71. }
  72. public void function2()
  73. {
  74. using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
  75. {
  76. //DoSomething();
  77. }
  78. }
    另一種方法就是使用SqlTransaction:
  79. using (SqlConnection conn = new SqlConnection(ConnectionStr))
  80. {
  81. conn.Open();
  82. SqlTransaction trans = conn.BeginTransaction();
  83. //DoSomething();
  84. trans.Commit();
  85. }
    不過遺憾的是這種方式不能實現事務嵌套,因此只能經過將trans做爲參數進行傳遞來實現事務處理。
    通過上面一系列的修改後,咱們的Person成了什麼樣子了?
  86. [DataObjectAttribute("Person", "ID", "")]
  87. public class Person
  88. {
  89. private int _ID;
  90. private string _Name;
  91. private int _Age;
  92. private string _Sex;
  93. private Head _Head;
  94. private Hand _LeftHand;
  95. private Hand _RightHand;
  96. [DataFieldAttribute("ID", true)]
  97. public int ID
  98. {
  99. get { return _ID; }
  100. set { _ID = value; }
  101. }
  102. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Head")]
  103. public Head Head
  104. {
  105. get { return _Head; }
  106. set { _Head = value; }
  107. }
  108. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
  109. public Hand LeftHand
  110. {
  111. get { return _LeftHand; }
  112. set { _LeftHand = value; }
  113. }
  114. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
  115. public Hand RightHand
  116. {
  117. get { return _RightHand; }
  118. set { _RightHand = value; }
  119. }
  120. [DataFieldAttribute("name", "NvarChar")]
  121. public string Name
  122. {
  123. get { return this._Name; }
  124. set { this._Name = value; }
  125. }
  126. [DataFieldAttribute("age", "int")]
  127. public int Age
  128. {
  129. get { return this._Age; }
  130. set { this._Age = value; }
  131. }
  132. [DataFieldAttribute("sex", "NvarChar")]
  133. public string Sex
  134. {
  135. get { return this._Sex; }
  136. set { this._Sex = value; }
  137. }
  138. }
  139. 固然了對於Person這樣的實體類,咱們徹底能夠本身寫代碼自動生成工具來弄,而後再作很小的修改就能夠了,這樣的工具實現簡單,我就不討論了。
    好了,關於個人MiniORM我就討論到這裏了,其它的請看代碼吧。
    ORM雖然是好東西,可是也存在不少方面的不足,首先咱們可以作到的是將大部分的數據庫操做交個ORM來作。另外少部分仍是須要咱們本身寫SQL的。單大部分的工做的分離能夠爲咱們節約大量的時間(也就是所謂的20/80原則,80%的教給ORM來處理,20%的本身作,固然很好了)。另外經過將這些相同的流程教給ORM來處理,能夠避免不少的疏忽致使的失誤(好比不當心把某個Insert,Update,Delete語句弄錯了什麼的)。
    最主要的缺點固然是性能問題,特別是個人MiniORM,所有采用反射來獲取映射規則,從而致使性能上更多的降低,不過咱們瞭解方法之後是很容易經過動態生成代碼,動態編譯的方式來減小這部分的性能損失的。另外某些部分的代碼顯得有些臃腫,特別是把判斷是否Indentity這樣的代碼放DataFieldAttribute中來處理(這個徹底能夠象DataFieldNotDoubleAttribute一分開處理的樣)等等。網絡

GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.com
來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=495app

相關文章
相關標籤/搜索