反射以及Attribute在ORM中的應用
一、 反射
什麼是反射?
簡單點吧,反射就是在執行時動態獲取物件資訊的方法,比如執行時知道物件有哪些屬性,方法,委託等等等等。
反射有什麼用呢?
反射不但讓你在執行是獲取物件的資訊,還提供執行時動態呼叫物件方法以及動態設定、獲取屬性等的能力。
反射在ORM中有什麼用呢?
我這裡所討論的ORM實現是通過自定義Attribute的方式進行對映規則的描述的。但是我們並不知道具體哪個物件需要對應哪個表,並且這些物件是獨立於我們的ORM框架的,所以我們只能通過自定義Attribute來定義對映規則,然後通過反射來動態獲取這些對映規則。
反射的實現:
下面我們就以簡單的獲取物件的屬性值的方式來做討論,假設我們有類Person,其中有3個屬性Name、Age,Sex。我們通過反射的方法來動態獲取Person的物件的這三個屬性的值。
public class Person { private string _Name; private int _Age; private string _Sex; public string Name { get { return this._Name; } set { this._Name = value; } } public int Age { get { return this._Age; } set { this._Age = value; } } public string Sex { get { return this._Sex; } set { this._Sex = value; } } }
測試程式碼如下:
static class Program { [STAThread] static void Main() { Person person = new Person(); person.Name = "snoopy"; person.Age = 5; person.Sex = "male"; PropertyInfo[] infos = person.GetType().GetProperties(); Console.WriteLine("列印屬性"); foreach (PropertyInfo info in infos) { //獲取屬性並列印 Console.WriteLine(info.Name + ":" + info.GetValue(person, null)); } Console.WriteLine("設定Person.Name = Hellokitty"); //設定屬性,設定Name屬性 foreach (PropertyInfo info in infos) { if (info.Name == "Name") { info.SetValue(person, "Hellokitty", null); } } Console.WriteLine("列印屬性"); foreach (PropertyInfo info in infos) { //獲取屬性並列印 Console.WriteLine(info.Name + ":" + info.GetValue(person, null)); } Console.Read(); } }
上面演示了通過反射的方法來動態獲取和設定物件屬性的方法。但是這和ORM以及Attribute有什麼關係呢?這個是我們接下來的這個部分的內容。
二、Attribute的使用:
Attribute中文翻譯雖然也號稱“屬性”,但是她和物件的屬性(Property)其實是完全不同的兩概念。她是在執行時對物件或者物件屬性、方法、委託等等進行描述的類,用於在執行時描述你的程式碼或者在執行時影響你的程式的行為。
其實我們在c#的程式設計中經常看到Attribute,只不過我們沒有注意罷了。比如Main函式前的“[STAThread]”這個其實就是一個Attribute。全程為[STAThreadAttribute]。另外指定類可序列化的[Serializable]等等。是不是都很熟悉啊?只不過平時估計沒有用到,所以沒有注意罷了。
既然Attribute是類,那麼她的定義方法和類就沒有兩樣了,唯一的不同就是自定義Attribute類必須繼承於System.Attribute。
下面我們來簡單定義一個描述資料庫欄位資訊的Attribute,在此類中我們採用更省略的方式,僅僅提供“欄位名”,“欄位型別”:
public class DataFieldAttribute : Attribute { private string _FieldName; private string _FieldType; public DataFieldAttribute(string fieldname, string fieldtype) { this._FieldName = fieldname; this._FieldType = fieldtype; } public string FieldName { get { return this._FieldName; } set { this._FieldName = value; } } public string FieldType { get { return this._FieldType; } set { this._FieldType = value; } } }
好,我們有了自己的描述資料庫欄位的Attribute,那麼我們現在將其應用到實際的類中。我們還是繼續上面的Person類,使用方法如下:
public class Person { private string _Name; private int _Age; private string _Sex; [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; } } }
通過自定義Attribute,我們定義了類屬性和資料庫欄位的一一對應關係,我們對Person類的Name、Age、Sex屬性都加上了Attribute的描述,指定了他們對應的欄位名以及型別,其中Person.Name對應於欄位name,欄位型別Nvarchar...。
三、反射和Attribute的聯合使用。
從上面的描述中,我們瞭解了反射,瞭解了Attribute,瞭解了ORM對映規則的定義。但是剛接觸的朋友估計還是迷惑,我們怎麼動態獲取這些對映規則呢?聽灑家慢慢道來。
這就需要使用反射了:
下面的例子,我們由於對Person中的Name,Age以及SEX都增加了DataFieldAttribute的描述,這其實就是增加了O(物件)/R(關聯式資料庫)的對映規則,下面我們就通過反射的方法來動態獲取此對映規則:
static class Program { [STAThread] static void Main() { Person person = new Person(); person.Name = "snoopy"; person.Age = 5; person.Sex = "male"; PropertyInfo[] infos = person.GetType().GetProperties(); object[] objDataFieldAttribute = null; foreach (PropertyInfo info in infos) { objDataFieldAttribute = info.GetCustomAttributes(typeof(DataFieldAttribute), false); if (objDataFieldAttribute != null) { Console.WriteLine(info.Name + "->資料庫欄位:" + ((DataFieldAttribute)objDataFieldAttribute[0]).FieldName); } } } }
哈哈,你是不是很想動手了啊?當然瞭如果你到了這一步就開始動手的話,那我就很高興了,說明我的描述還算清楚(注:對於已經知道的大牛們此話無效)。也說明你很有動手的能力。因為接下來的工作就是怎樣根據這種方法動態地從物件中獲取對映規則,動態構造Insert,Update,Delete等語句。
四、本章總結
本章中我比較詳細地介紹了反射,自定義Attribute的概念和應用,並且介紹了怎樣在執行時動態獲取O/R Mapping的對映規則等。當然我這裡的程式碼僅僅是舉例,而要真正實現一個ORM,我們還需要考慮的很多,比如:
1、Person對應於哪張資料庫表?
2、Person中的PK和FK(如果有的話)怎麼表示?
......
這些問題將在我的下一篇中進行講解。
原文連結:
C#基礎系列:實現自己的ORM(反射以及Attribute在ORM中的應用)
相關連線:
C#基礎系列:實現自己的ORM(MiniORM的測試程式碼)
C#基礎系列:開發自己的窗體設計器(PropertyGrid顯示中文屬性名)