構造我本身的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:負責根據實體對象,讀取指定的記錄;服務器

上面就是MiniORM的3個主要類。下面咱們就詳細地根據前面的描述一步步構造她。咱們這裏仍是之前面說的Person爲例進行說明。網絡

經過本系列第一章,咱們知道,對象不但存在繼承關係,特別在實際的應用中還存在包含關係,好比一個Person包含兩個Hand(手)類,包含一個Head(頭)類等,咱們的Person在數據庫中應該有一個ID,爲了更加方便使用和討論,此ID在MiniORM中是一個int以及自動增加類型(ID INDENTITY(1,1))。這些都是咱們的MiniORM應該考慮的範圍。
咱們對咱們的Person作修改:app

  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.  
  12. public int ID
  13. {
  14. get { return _ID; }
  15. set { _ID = value; }
  16. }
  17.  
  18. public Head Head
  19. {
  20. get { return _Head; }
  21. set { _Head = value; }
  22. }
  23.  
  24. public Hand LeftHand
  25. {
  26. get { return _LeftHand; }
  27. set { _LeftHand = value; }
  28. }
  29.  
  30. public Hand RightHand
  31. {
  32. get { return _RightHand; }
  33. set { _RightHand = value; }
  34. }
  35.  
  36. [DataFieldAttribute("name", "NvarChar")]
  37. public string Name
  38. {
  39. get { return this._Name; }
  40. set { this._Name = value; }
  41. }
  42.  
  43. [DataFieldAttribute("age", "int")]
  44. public int Age
  45. {
  46. get { return this._Age; }
  47. set { this._Age = value; }
  48. }
  49.  
  50. [DataFieldAttribute("sex", "NvarChar")]
  51. public string Sex
  52. {
  53. get { return this._Sex; }
  54. set { this._Sex = value; }
  55. }
  56. }

你可能又發現了一個問題,就是在咱們修改後的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類型運維

  1. [DataFieldAttribute("ID", true)]
  2. public int ID
  3. {
  4. get { return _ID; }
  5. set { _ID = value; }
  6. }

三、怎樣標誌字段是類對象(好比Person中的Hand,固然複雜點的對象,可能包含子對象列表)?
因爲MiniORM提供的是一個相似框架的東西,因此不該該受到實體類的限制,因此對於類對象字段,咱們應該可以描述此對象所在的程序集,命名空間,類名,這樣咱們才能夠運行時建立該對象。工具

  1. public class SubDataObjectAttribute : Attribute
  2. {
  3. private SubDataObjectFieldType _FieldType;
  4. private string _AssemblyName;
  5. private string _NamespaceName;
  6. private string _ClassName;
  7.  
  8. public SubDataObjectAttribute(SubDataObjectFieldType fieldtype, string assemblyname, string namespacename, string classname)
  9. {
  10. this._FieldType = fieldtype;
  11. this._AssemblyName = assemblyname;
  12. this._NamespaceName = namespacename;
  13. this._ClassName = classname;
  14. }
  15.  
  16. /// <summary>
  17. /// 本記錄對應的FieldType
  18. /// </summary>
  19. public SubDataObjectFieldType FieldType
  20. {
  21. get { return _FieldType; }
  22. }
  23.  
  24. /// <summary>
  25. /// 本記錄對應的AssemblyName
  26. /// </summary>
  27. public string AssemblyName
  28. {
  29. get { return _AssemblyName; }
  30. }
  31.  
  32. /// <summary>
  33. /// 本記錄對應的NamespaceName
  34. /// </summary>
  35. public string NamespaceName
  36. {
  37. get { return _NamespaceName; }
  38. }
  39.  
  40. /// <summary>
  41. /// 本記錄對應的ClassName
  42. /// </summary>
  43. public string ClassName
  44. {
  45. get { return _ClassName; }
  46. }
  47. }

其中SubDataObjectFieldType是一個枚舉類型,由於咱們的子對象多是單獨的對象好比Person.Head也多是一個列表(List)。因此我增長了這個枚舉類型,用於作標誌。性能

  1. public enum SubDataObjectFieldType
  2. {
  3. Object,
  4. /// <summary>
  5. /// 本字段屬於List類型,直接遍歷
  6. /// </summary>
  7. List,
  8. }

固然了,這裏的子對象列表多是ArrayList,HashTable等等,你均可以根據本身項目中實際使用到的類型來作相應的修改。網站

四、怎麼控制某個字段在表中不能重複?
好比咱們要控制Person.Name不能重複,若是你新增的時候發現重複要提示。那咱們也經過增長一個Attribute的形式來實現。這個Attribute很簡單,沒有任何方法和屬性。

  1. public class DataFieldNotDoubleAttribute : Attribute
  2. {
  3. }

五、怎樣作事務處理?
事務處理是每一個底層框架都應該考慮到的問題,在.NET中咱們有兩種方式來進行事務處理,一種是使用COM+,這是最好的方法,不過性能上比較欠缺,另外這東西配置很麻煩,當你數據庫安裝在另一太服務器上的時候,每每出現沒法使用的問題,我曾經就被這東西折騰夠嗆,因此我乾脆就不用他了,不過仍是介紹下語法,經過使用TransactionScope就能夠很好的使用com+提供的事務處理,代碼至關的簡潔,優美,只惋惜啊!天使的面孔,魔鬼的心。

  1. public void function1()
  2. {
  3. using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
  4. {
  5. function2();
  6. }
  7. }
  8.  
  9. public void function2()
  10. {
  11. using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
  12. {
  13. //DoSomething();
  14. }
  15. }

另一種方法就是使用SqlTransaction:

  1. using (SqlConnection conn = new SqlConnection(ConnectionStr))
  2. {
  3. conn.Open();
  4. SqlTransaction trans = conn.BeginTransaction();
  5. //DoSomething();
  6. trans.Commit();
  7. }

不過遺憾的是這種方式不能實現事務嵌套,因此只能經過將trans做爲參數進行傳遞來實現事務處理。

通過上面一系列的修改後,咱們的Person成了什麼樣子了?

  1. [DataObjectAttribute("Person", "ID", "")]
  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.  
  12. [DataFieldAttribute("ID", true)]
  13. public int ID
  14. {
  15. get { return _ID; }
  16. set { _ID = value; }
  17. }
  18.  
  19. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Head")]
  20. public Head Head
  21. {
  22. get { return _Head; }
  23. set { _Head = value; }
  24. }
  25.  
  26. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
  27. public Hand LeftHand
  28. {
  29. get { return _LeftHand; }
  30. set { _LeftHand = value; }
  31. }
  32.  
  33. [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
  34. public Hand RightHand
  35. {
  36. get { return _RightHand; }
  37. set { _RightHand = value; }
  38. }
  39.  
  40. [DataFieldAttribute("name", "NvarChar")]
  41. public string Name
  42. {
  43. get { return this._Name; }
  44. set { this._Name = value; }
  45. }
  46.  
  47. [DataFieldAttribute("age", "int")]
  48. public int Age
  49. {
  50. get { return this._Age; }
  51. set { this._Age = value; }
  52. }
  53.  
  54. [DataFieldAttribute("sex", "NvarChar")]
  55. public string Sex
  56. {
  57. get { return this._Sex; }
  58. set { this._Sex = value; }
  59. }
  60. }
  61.  
  62.  

固然了對於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=495

相關文章
相關標籤/搜索