一、應用場景
假設我們有一張數據表Student,並且有以下字段
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Grade{ get; set; }
}
當我們在寫數據訪問層的時候,需要根據Id獲取數據,那我們就很自然的寫一個方法 GetStudentById(int Id);假設需求還需要根據Name來獲取數據,我們也很自然的再寫另一個方法GetStudentByName(string name);此時,項目中,還需要一個根據年紀來查詢數據的方法,或者根據Name和Grade來查詢數據的方法,我們也會新增一個數據訪問層方法或者進行方法重載GetStudent(string name,int Grade);
而在我們寫的這些方法中,幾乎99%的代碼是相同的,唯一不同的僅僅是Sql語句后的where條件而已,所以,我今天就想,有沒有一種方法,我傳入一個Student對象,方法自己判斷我需要查詢哪些字段呢?當然有,那就是反射了,不過,事后我發現還是有一點點缺陷,文章末尾會說到!
二、"擼"起袖子,打開Visual Studio
Demo開始前,你需要具備一點點的反射基礎:
1、using System.Reflection; 絕大部分使用的方法在這個命名空間中,有興趣的可以使用Reflector反編譯研究下源碼
2、Type t = object.GetType(); 獲取當前實例的類型描述信息,從而描述信息當中,我們就可以很容易知道當前實例有哪些方法、屬性等等……
3、 PropertyInfo[] infos = t.GetProperties(); 獲取從當前實例得到的描述信息中得到相關的屬性數組,也就是這里可以得到Id,Name,Grade;
三、Demo思路
數據訪問層的查詢方法,接受一個Student實例參數,在調用該方法前,我們在業務邏輯層中實例化一個Student對象,並根據查詢條件對該實例的屬性進行賦值,比如,我想根據Id等於1的學生信息,那就在實例化對象后,對Id進行賦值即可。
具體思路:
(1)利用反射,獲取條件參數實例中的屬性數組;
(2)遍歷該屬性數組,對當前屬性的值進行判斷,如果為空,則不在查詢條件中,如果不為空,則是業務邏輯層想要的查詢條件;
(3)接上步,如果不為空,則獲取當前屬性的名稱、Value,進行條件的拼接,裝載在集合中;
(4)遍歷完畢,對集合進行join拼接;
private Student GetStudent(Student stu) { //獲取當前實例的描述信息 Type t = stu.GetType(); //從描述信息當中,獲取當前實例的屬性數組 PropertyInfo[] infos = t.GetProperties(); //用於裝載最終的查詢條件的集合 List<string> list = new List<string>(); for (int i = 0; i < infos.Length; i++) { //當前屬性的值不為null,則證明是業務邏輯層中想要的查詢條件 if (infos[i].GetValue(stu, null) != null) { string temp = string.Format("{0}='{1}'", infos[i].Name, infos[i].GetValue(stu, null)); list.Add(temp); } } //拼接sql語句 string s = string.Join("and", list); //todo:拼接SQL語句,進行數據庫查詢 return null; }
四、驗證
假如,我想要查詢Id=1並且Name=dotnetgeek的條件的學生記錄,那我們就要在調用數據訪問層方法前,進行初始對象實例化,並且對屬性進行賦值;
Student s = new Student(); s.Id = 1; s.Name = "dotnetgeek"; //調用數據訪問層方法 GetStudent(s);
則最終拼接出來的SQL條件則會是如圖所示:(這里只演示到拼接SQL語句的步驟,進行數據查詢不是本文章討論范圍)

五、Bug
不知道大家有沒有發現到,查詢條件里面出現了一個Bug,請留意我調用方法前是對Id,Name屬性進行賦值的,最終SQL條件里又多了一個Grade屬性,這里因為實體類中,Grade是int類型,是int類型的話就會默認值為0,所以,如果想用這種解決方法的話,還需在判斷為null的地方再判斷不能為0,but,如果用戶想查詢該字段等於0的記錄怎么辦?
六、總結
這個Demo是我在編碼中浮想出來的,當時我已經寫了2個功能相同的方法,加上同事已經寫過1個相類似的方法,整個數據訪問層中已經出現了相似的三個方法了,假如其他同事又有別的業務需求,是不是要再寫第4個,第5個,第……個呢?所以我就想有沒有一種方法可以實現一下,再說,在Entity Framework中,進行刪除操作也是傳入一個實體模型的,如果有興趣的可以去研究下EF的源碼!
