从最初做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与上表各字段对应
现在我们需要为这个表写一个访问类,提供对数据库的增、删、改、查工作。
下面是实现以上功能的所有代码:
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 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);
}
}
最后就是对上面类各个方法的调用了,以实现项目的需求。
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功能涉及到另一个类库,所以在附件中去除了,需要的可以自己加入。
几个主要类源码: