從最初做JAVA開發到現在致力於.NET平台,經手的大大小小的項目也不在少數。剛開始每寫成一個程序還能獲得不少的成就感,但隨著開發年限的增加、項目代碼行的增加,寫代碼更多時候變成一種糊口的工具而非興趣時,越來越被大量相似的SQL攪得頭暈腦脹,尤其維護舊程序時,對SQL的調試更是讓人頭大。於是,一直致力於找出一個方法,可以不用每次都去編寫那些煩人的SQL。
答案是肯定的,JAVA平台下有個Hibernate的ORM框架,它應該是最早的一個成熟的ORM軟件,貌似可以解決我的問題。迫不及待的下載、使用MyEclipse去寫測試程序,卻發現並不如想象中的好,對我來說,最大的障礙在於那個XML配置文件。借助於IDE,配置工作或許還不是太繁瑣,但脫離IDE之後,我發現對這個配置文件維護的困難絕對不在直接維護SQL命令之下。當然,好處還是有的,比如不用去調試每一條SQL。
雖然不太情願,但畢竟脫離IDE的情況還是很少,所以也就“愛用不用”地用了一段時間。
之後,機緣巧合的轉向.NET平台,從.NET Framework 1.1和VS2003開始,到現在的.NET Framework 2.0和VS2005,隨著在.NET平台開發經驗的日漸豐富,對C#特性越來越全面的了解,終於決定要自己寫一個更加符合C#口味的ORM框架,完全的面向對象,更加簡潔,更加高效,對IDE的智能感知支持更好(早期綁定) 。
一起來看看它的工作方式。
假設有一個員工表,字段為 id,employee_id,name,department_id
有一個實體類EmployeeInfo與上表各字段對應
現在我們需要為這個表寫一個訪問類,提供對數據庫的增、刪、改、查工作。
下面是實現以上功能的所有代碼:
public class EmployeeTable : TableAccess<EmployeeInfo> {
internal override ColumnParameter[] FillColumnParameters(EmployeeInfo entity) {
return base.BuildColumnParameter(
entity.ID,
entity.EmployeeID,
entity.Name,
entity.DepartmentID
);
}
public override string TableName {
get { return "employee_table"; }
}
public override TableColumnCollection TableColumns {
get {
TableColumnCollection list = new TableColumnCollection(4);
list.AddRange(new TableColumn[] {
new TableColumn("id", true), // 自增只讀字段
new TableColumn("employee_id"),
new TableColumn("name"),
new TableColumn("department_id")
});
return list;
}
}
internal override EmployeeInfo BuildEntity(MySqlDataReader reader, int startIndex) {
EmployeeInfo info = new EmployeeInfo();
info.ID = reader.GetInt32(0);
info.EmployeeID = reader.GetString(1);
info.Name = reader.GetString(2);
info.DepartmentID = DataReadyUtility.GetInt32(reader,3);
return info;
}
}
從上面的代碼可以看出,對一個獨立表的訪問,只需重寫抽象基類的4個方法即可,可實現以下功能:
* 按主鍵獲取一個數據行 GetByPrimaryKey(object)
* 獲取數據表的所有記錄 GetAll()
* 查詢 Search(SearchConditionCollection, ...)
* 分頁查詢 Search(SearchConditionCollection, int, int, ...)
* 保存 Save(EmployeeInfo)
* 按主鍵值修改 Modify(...)
* 按條件修改 Modify(SearchConditionCollection, ...)
* 按主鍵刪除 Delete(...)
* 按查詢條件刪除 Delete(SearchConditionCollection, ...)
下面的代碼是對EmployeeTable類的擴充,使它具有足夠多的功能:
public class EmployeeTable : TableAccess<EmployeeInfo> {
// 修改員工名字
public int ChangeName(int id, string name) {
EmployeeInfo info = new EmployeeInfo();
info.Name = name;
// 使用ModifyBounds.None表示僅修改後面指定的字段 TableColumns["name"]
return base.Modify(id, info, ModifyBounds.None, TableColumns["name"]);
}
// 按名字獲取員工,不分頁
public IList<EmployeeInfo> GetByName(string name) {
SearchConditionCollection filter = new SearchCoditionCollection();
// 按Like查詢name字段
filter.add(new SearchCondition("name", new ColumnComparison(SqlOperator.Like, name)));
return base.Search(filter);
}
// 按名字獲取員工,並分頁 。輸出不分頁情況下的記錄總數,供前台分頁使用
public IList<EmployeeInfo> GetByName(string name, int pageSize, int pageIndex, out int itemCount) {
SearchConditionCollection filter = new SearchCoditionCollection();
// 按Like查詢name字段
filter.add(new SearchCondition("name", new ColumnComparison(SqlOperator.Like, name)));
return base.Search(filter, pageSize, pageIndex, out itemCount);
}
}
最後就是對上面類各個方法的調用了,以實現項目的需求。
public class EmployeeTableTest {
void Test() {
EmployeeTable employeeDAL = new EmployeeTable();
SearchConditionCollection filter = new SearchConditionCollection(); // 查詢條件生成器
// 保存一個員工
EmployeeInfo employee = new EmployeeInfo();
employee.Name = "wfyfngu";
employee.DepartmentID = 4;
employeeDAL.Save(employee);
// 獲取ID為1的員工
EmployeeInfo info = employeeDAL.GetByPrimaryKey(1);
// 查詢所有員工
IList<EmployeeInfo> all = employeeDAL.GetAll();
// 分頁查詢
int itemCount;
IList<EmployeeInfo> pagedSource = employeeDAL.Search(null,10,1,out itemCount);
// 修改ID為1的員工的名字
employeeDAL.ChangeName(1, "wfyfngu");
// 考慮到項目分層,業務邏輯層應該對數據庫字段一無所知
// 所以,下面演示的方法僅僅出於演示目的,將字段名硬編碼到方法中
// 刪除ID為 1 2 5 7 的用戶
filter.Add(new SearchCondition(
"user_id",
new ColumnComparison(SqlOperator.In, 1,2,5,7)
));
employeeDAL.Delete(filter);
// 查詢名字以wfy開頭的員工,並分頁
filter.Clear();
filter.Add(new SearchCondition(
"name",
new ColumnComparison(SqlOperator.StartWith, "wfy")
));
IList<EmployeeInfo> result = employeeDAL.Search(filter,10,1,out itemCount);
}
}
其它功能,如簡單的聯表查詢,視圖查詢等不一一列舉。
附件說明:附件為本人編寫的ORM替代框架,針對MySql編寫,由於Cache功能涉及到另一個類庫,所以在附件中去除了,需要的可以自己加入。
幾個主要類源碼:
ReadonlyTableAccess<T>
TableAccess<T>
JoinTableAccess<T>
本文配套源碼