构造我本身的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

相关文章
相关标签/搜索