.NET框架下ORM的一个轻量高效替代方案

从最初做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>

框架下载地址:https://files.cnblogs.com/wfyfngu/SqlUtility.zip

原文地址:https://www.cnblogs.com/wfyfngu/p/1657762.html