程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#基礎系列:實現自己的ORM(構造我自己的ORM)

C#基礎系列:實現自己的ORM(構造我自己的ORM)

編輯:關於C#

構造我自己的ORM

通過前面兩章的描述,我相信很多朋友都已經明白 我了下面將要討論到的ORM的實現方法了,那就是根據自定義Attribute來定義 O/R Mapping規則,然後通過反射來動態獲取此規則,動態構造SQL語句。

由於這個小東西(ORM)出生在深圳,所以我想來想去,她應該有個深圳 的名字,所以我就叫她“MiniORM”。不知道各位認為如何?

MiniORM采用的是ONE_INHERIT_TREE_ONE_CLASS(一個繼承樹對應於一個 表)的結構,雖然這種結構容易導致數據冗余,但是這種結構很簡單。另,本 MiniORM 僅僅考慮一個表一個PK,一個FK的情況。

MiniORM結構如下,為 了更便於理解和使用,我使用了3個類:

1、OrmWriter:負責將實體對象 (比如前面章節說的Person)插入數據庫和修改數據庫中對應的記錄。

2 、OrmRemover:負責根據實體對象,刪除指定的記錄;

3、OrmReader: 負責根據實體對象,讀取指定的記錄;

上面就是MiniORM的3個主要類。下面我們就詳細地根據前面的描述一 步步構造她。我們這裡還是以前面說的Person為例進行說明。

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

我們對我們的Person做修改:

[DataObjectAttribute("Person")]
public class Person
{
  private int _ID;
  private string _Name;
   private int _Age;
  private string _Sex;
  private Head _Head;
  private Hand _LeftHand;
  private Hand _RightHand;
  public int ID
  {
    get { return _ID; }
    set { _ID = value; }
  }
   public Head Head
  {
    get { return _Head; }
     set { _Head = value; }
  }
  public Hand LeftHand
  {
    get { return _LeftHand; }
     set { _LeftHand = value; }
  }
  public Hand RightHand
  {
    get { return _RightHand; }
     set { _RightHand = value; }
  }
   [DataFieldAttribute("name", "NvarChar")]
   public string Name
  {
    get { return this._Name; }
    set { this._Name = value; }
  }
   [DataFieldAttribute("age", "int")]
  public int Age
  {
    get { return this._Age; }
     set { this._Age = value; }
  }
  [DataFieldAttribute ("sex", "NvarChar")]
  public string Sex
  {
    get { return this._Sex; }
    set { this._Sex = value; }
  }
}

你可能又發現了一個 問題,就是在我們修改後的Person中,增加了LeftHand,RightHand以及Head, 可是這三個都屬於類啊,這個怎麼能夠保存到數據庫中呢?並且使用我們前面的 DataFieldAttribute是沒有辦法描述的啊。另外還增加了個ID,又怎麼來標志這 個是自動增長的int型PK呢?當然了能夠到這裡你就發現這些問題那是相當的不 錯了。如果前面就動手的人,估計考慮的還是修改我們的DataFieldAttribute讓 它能夠對這些東西進行區別。比如在DataFieldAttribute中再增加一個屬性用於 區別哪個是ID屬性,哪個是對象類型(比如Hand)屬性。這當然是好的,只不過 這樣做導致我們的代碼極其丑陋。最好的辦法還是另外增加一個Attribute。當 然了,我是為了更加方便的構造SQL語句,我做的不是很好。

1、怎麼表 示實體類對應的數據庫表的PK和FK?

為了更方便的實現,MiniORM中標志 一個實體類的PK和FK都是在DataObjectAttribute中來做(其實最好的辦法還是 另外增加個比如PKAttribute和FKAttribute,不過這個留給其它人去做吧)。如 下,DataObjectAttribute第一個參數表示對應的數據庫表,第二個參數表示PK ,第三個參數表示FK:

[DataObjectAttribute ("Person", "ID", "")]
public class Person
{
  ......
}
2、怎麼標志字段是Indentity (自動增長)?

在DataFieldAttribute中增加了個屬性,用於標志某個 字段是否自動增長的字段。這些都是我個人懶做的,其中,第二個參數標志ID是 Identity類型

[DataFieldAttribute("ID", true)]
public int ID
{
  get { return _ID; }
  set { _ID = value; }
}

3、怎樣標志字段是類對象(比如Person中的 Hand,當然復雜點的對象,可能包含子對象列表)?

由於MiniORM提供的 是一個類似框架的東西,所以不應該受到實體類的限制,所以對於類對象字段, 我們應該能夠描述此對象所在的程序集,命名空間,類名,這樣我們才可以運行 時創建該對象。

public class SubDataObjectAttribute : Attribute
{
  private SubDataObjectFieldType _FieldType;
  private string _AssemblyName;
  private string _NamespaceName;
  private string _ClassName;
   public SubDataObjectAttribute(SubDataObjectFieldType fieldtype, string assemblyname, string namespacename, string classname)
  {
    this._FieldType = fieldtype;
    this._AssemblyName = assemblyname;
    this._NamespaceName = namespacename;
    this._ClassName = classname;
  }
  /// <summary>
  /// 本記錄對應的FieldType
  /// </summary>
  public SubDataObjectFieldType FieldType
  {
    get { return _FieldType; }
  }
   /// <summary>
  /// 本記錄對應的AssemblyName
   /// </summary>
  public string AssemblyName
   {
    get { return _AssemblyName; }
  }
  /// <summary>
  /// 本記錄對應的NamespaceName
  /// </summary>
  public string NamespaceName
  {
    get { return _NamespaceName; }
  }
  /// <summary>
  /// 本記錄對應的ClassName
  /// </summary>
  public string ClassName
  {
     get { return _ClassName; }
  }
}

其中 SubDataObjectFieldType是一個枚舉類型,因為我們的子對象可能是單獨的對象 比如Person.Head也可能是一個列表(List)。所以我增加了這個枚舉類型,用 於做標志。

public enum SubDataObjectFieldType
{
  Object,
  /// <summary>
  /// 本字段屬於List類 型,直接遍歷
  /// </summary>
  List,
}
當然了,這裡的子對象列表可能是ArrayList,HashTable等等,你都可 以根據自己項目中實際使用到的類型來做相應的修改。

4、怎麼控制某個 字段在表中不能重復?

比如我們要控制Person.Name不能重復,如果你新 增的時候發現重復要提示。那我們也通過增加一個Attribute的形式來實現。這 個Attribute很簡單,沒有任何方法和屬性。

public class DataFieldNotDoubleAttribute : Attribute
{
}

5、 怎樣做事務處理?

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

public void function1()
{
  using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope (System.Transactions.TransactionScopeOption.Required))
  {
    function2();
  }
}
public void function2()
{
  using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope (System.Transactions.TransactionScopeOption.Required))
  {
    //DoSomething();
  }
}
另外一種方法就是使 用SqlTransaction:

using (SqlConnection conn = new SqlConnection(ConnectionStr))
{
  conn.Open();
   SqlTransaction trans = conn.BeginTransaction();
   //DoSomething();
  trans.Commit();
}
不過遺憾的是 這種方式不能實現事務嵌套,所以只能通過將trans作為參數進行傳遞來實現事 務處理。

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

[DataObjectAttribute("Person", "ID", "")]
public class Person
{
  private int _ID;
  private string _Name;
  private int _Age;
   private string _Sex;
  private Head _Head;
  private Hand _LeftHand;
  private Hand _RightHand;
   [DataFieldAttribute("ID", true)]
  public int ID
  {
    get { return _ID; }
    set { _ID = value; }
  }
  [SubDataObjectAttribute (SubDataObjectFieldType.Object, "Person", "Person", "Head")]
  public Head Head
  {
    get { return _Head; }
    set { _Head = value; }
  }
  [SubDataObjectAttribute (SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
  public Hand LeftHand
  {
    get { return _LeftHand; }
     set { _LeftHand = value; }
  }
   [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
   public Hand RightHand
  {
    get { return _RightHand; }
    set { _RightHand = value; }
  }
   [DataFieldAttribute("name", "NvarChar")]
   public string Name
  {
    get { return this._Name; }
    set { this._Name = value; }
  }
   [DataFieldAttribute("age", "int")]
  public int Age
  {
    get { return this._Age; }
     set { this._Age = value; }
  }
  [DataFieldAttribute ("sex", "NvarChar")]
  public string Sex
  {
    get { return this._Sex; }
    set { this._Sex = value; }
  }
}

 

當然了對於 Person這樣的實體類,我們完全可以自己寫代碼自動生成工具來弄,然後再做很 小的修改就可以了,這樣的工具實現簡單,我就不討論了。

好了,關於 我的MiniORM我就討論到這裡了,其它的請看代碼吧。

ORM雖然是好東西 ,但是也存在很多方面的不足,首先我們能夠做到的是將大部分的數據庫操作交 個ORM來做。另外少部分還是需要我們自己寫SQL的。單大部分的工作的分離可以 為我們節約大量的時間(也就是所謂的20/80原則,80%的教給ORM來處理,20%的 自己做,當然很好了)。另外通過將這些相同的流程教給ORM來處理,可以避免 很多的疏忽導致的失誤(比如不小心把某個Insert,Update,Delete語句弄錯了 什麼的)。

最主要的缺點當然是性能問題,特別是我的MiniORM,全部采 用反射來獲取映射規則,從而導致性能上更多的下降,不過我們了解方法以後是 很容易通過動態生成代碼,動態編譯的方式來減少這部分的性能損失的。另外某 些部分的代碼顯得有些臃腫,特別是把判斷是否Indentity這樣的代碼放 DataFieldAttribute中來處理(這個完全可以象DataFieldNotDoubleAttribute 一分開處理的樣)等等。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved