介绍一下本人最近在实现的数据库访问框架,我认为它能够简化数据库访问的复杂性。
别骂我在这个ORM漫天飞的时代,还多搞个ORM出来添乱,我只是希望大家来讨论一下我这个思路。
我暂时给这个框架取名为Pool,其中的核心类叫Fisher。
上次说过ADO.NET在Pool中的简化,这次说ORM部分。简单举个例子,数据库中的实体表:Clazz 班级表
  Id int 主键 自动增长
  Name nvarchar(50)Student 学生表
  Id int 主键 自动增长
  Age int
  Name nvarchar(50)
  ClazzId int 外键关联班级表
程序中的实体类:public class Clazz{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Student> Students { get; set; }//外键学生对象的集合
}
public class Student{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual int Age { get; set; }
    public virtual Clazz Clazz { get; set; }//外键关联班级
}相比其他很多框架,Pool的一大亮点就是尽量少的配置。
这么明摆的对应,还需要任何多余的配置吗?
如果需要Pool的配置暂时还没有专业的配置文件。
配置写在.NET程序的配置文件中,也就是App.Config或者Web.Config文件中。
在ConnectionStrings节点下添加项<!--配置Clazz实体类对应表名Clazz-->
<add name="Clazz" connectionString="Name=Clazz"/>
<!--配置Clazz.Name对应列名Name-->
<add name="Clazz.Name" connectionString="Name=Name"/>这些配置不是必须的,Pool会自动判断最一般的情况。增://添加一个班级名为"丐班"
Clazz clazz=new Clazz();
clazz.Name="丐班";
Fisher.TheFisher.Add(clazz);Pool会自动从数据库中获取插入的Id,填充到clazz对象中。
因为对sqlserver做了特殊处理。
Pool提供了一套通用的配置办法。<!--sqlserver中-->
<add name="Clazz.Id" connectionString="Getter=@@Identity"/>
<!--oracle中用序列-->
<add name="Clazz.Id" connectionString="Default=sClazzId.nextval;Getter=sClazzId.currval"/>节点中Default表示使用默认值插入,Getter表示如何获取刚才插入的默认值。
也就是生成的sql语句为:--sqlserver
insert Clazz(Name)values(@Age)
select * from Student where Id=@@Identity
--oracle
insert Clazz(Id,Name)values(sClazzId.nextval,:Name)
select * from Student where Id=sClazzId.currval如果用配置方案,对任何数据库通用。
即使要插入的列不是主键,也可以使用数据库的默认值。删://删除clazz对应的数据记录
theFisher.Del(clazz);
//删除Id为1的Clazz数据记录
theFisher.Del<Clazz>(1);Pool提供2套删除策略,1是删除对象,2是根据Id。
删除Clazz的时候,会连带把Clazz对应的Student对象给删了。
(这个连级删除希望大家给点意见,是不是应该连级?)改:clazz.Name="斧头班";
theFisher.Edit(clazz);修改不会导致连级修改,根据我之前在CSDN发的帖子调查,
认同连级修改会造成项目的混乱,也就是修改clazz对象时,
clazz.Students不会进行修改。查://查询Id为1的班级
Clazz clazz=theFisher.Get<Clazz>(1);
//使用sql查询,参数使用方式和ADO.NET部分类似
//(sql,参数名1,参数值1,参数名2,参数值2....)
Clazz clazz=theFisher.First<Clazz>("select * from Clazz where Name=@Name","@Name","斧头班");
//查询集合
IList<Clazz> clazzs=theFisher.Find<Clazz>("select * from Clazz where Name like '%班'");
//使用分页 表示从第10开始,查询10条记录
IList<Clazz> clazzs=theFisher.Find<Clazz>(10,10,"select * from Clazz");Pool没有定义如hibernate的HQL类似的查询语言,
使用的是sql查询,虽然有备OO,但是我认为这样提供了更加强大的查询优化空间。Pool中使用懒加载,如查询出student,那么student.clazz不会马上被加载,只有用到才会加载。
这样在关系复杂的数据库中提高了不至于查询一个实体,关联出大半个数据库来。有时候不需要查询全部的字段,如果硬要将实体整个查处,对于性能是浪费。//只查询了student的Id和年龄
IList<Student> students=theFisher.Find<Student>("select Id,Age from Student");这样Pool只会填充Id,Age字段信息。你也可以用存储过程查询:
设有存储过程proc1,内容如下:select * from Student那么Pool也能够:IList<Student> students=theFisher.Find<Student>("proc1");
大致介绍到这里吧

解决方案 »

  1.   

    上次发的简化ADO.NET的部分老是被人误解成,就是Pool的ORM
    怎么写的比手写ADO.NET没差这张贴子才是Pool的ORM部分。
      

  2.   

    大家看看我的ORM框架:比LINQ简单:PDF.NET框架之OQL语言!
    http://topic.csdn.net/u/20100328/18/12fdb814-3882-4613-be85-4b17b05dd4ab.html?seed=815559937&r=64634534#r_64634534 [推荐] 不使用反射的实体类,大家评评?
    http://topic.csdn.net/u/20100125/18/d6738f2f-5581-4c8c-8c82-9bd8c2f256c8.html
      

  3.   

    如果student.Class不能为空, 是必须要联级删除的。但是考虑有时不能随便就把学生开除了, 可以加上限制, 当clazz.Students.Count > 0 不允许删除。或者可以给基类加一个属性CanDeleteWhileNoEmpty, 值为Ture的时候一起干掉, 不为Ture的时候先提示把子集清空
      

  4.   


    谢谢你的建议。你的意思是说多家一个属性判断是否要删除嘛?
    这个做法好像类似hibernate可配置的。不过我想在想是不是吧判断这部分留给业务逻辑判断呢?
      

  5.   

    这里业务逻辑判断好像有点难。举个例子, 有教学楼, 里面有教室若干。 教室必定是从属某一教学楼的。 也就是说buildID不能为空。 当拆除教学楼的时候, 教室也就没了, 所以是联级全部删除。再如上文学生和班级, 学生必定分在某一班, classID同样不能为空。但是有时师资力量不够, 三个班并成二个班的,我们就不能直接把三班干掉, 而要让学生先分到别的班去。
      

  6.   

    我想, 大致可以分成三种情况。第一种, 不能删除子集。 当关系是多对多的时候是肯定不能删除的。 比如教师和课程, 不能教师下海了, 学生就不开这门课了。第二种, 子集一定要删除, 就像上贴说的教学楼被拆。第三种, 只有子集为空时才能删除。如果在业务逻辑上每次都进行判断是否删除子集, 还不如在类中定义这个开关属性, 这样重写的代码量最少。 因为你只需要在Del里判断一次开关就可以。
      

  7.   

    我倒是想到一种情况想看下你怎么实现ORM, 最近也在研究这个。一个网站, 有角色, 权限两个类, 每个角色可以有多个权限, 权限可以分配给多个角色。一般的, 我们会设计三个表:Role(RoleID, RoleName)
    Privilege(PrivalegeID, PrivilegeName)
    RolePrivilge(RoleID, PrivalegeID)这个类要怎么设计, 还有删除角色或权限的时候怎么自动把第三个表里的记录清除。
      

  8.   


    你这个问题值得考虑。我不喜欢使用多对多关联,我会分解成一对多,和多对一的。
    所以在类的设计上和一般的情况不会有差别。也就是class Role
      RoleID
      RoleName
      //加一个外键集合
      IList<RolePrivilge> RolePrivilgesclass Privilege
      PrivalegeID,
      PrivilegeName
      //也有一个外键集合
      IList<RolePrivilge> RolePrivilgesclass RolePrivilge
      //使用外键引用
      Role
      Privileg
    大概也就是分解成一对多和多对一,
    然后原来什么样现在还是什么样。
      

  9.   

    Pool的效率不是问题,你也看了测试结果了,和手写ado.net速度相当。有空继续多讨论
      

  10.   

    问题还是出在DataReader.GetValue 上面,还在想解决方案。
      

  11.   

    Pool的一些特性:
    简单
    少配置(大多数情况不需要配置)
    懒加载
    高效率(效率比普通手写ADO.NET相当,甚至更优秀)
    能够使用原始sql
      

  12.   

    抱歉!没看完!
    就访问数据库是否简单来看,
    ObjectDataSource数据源控件+低层强类型的DataSet,
    已经可以做到不写一行代码,实现对表的增,删,改,查!
      

  13.   

    拉个datasource或者是LINQ然后搞个DATAGRIDVIEW然后设置下属性,然后就OK了!
    多简单。
      

  14.   

    这样也就比较适合单表操作吧。
    一个项目下来,这样能满足所有需求吗?
    Pool绝不是仅仅为了简单。我想很多人认为这又是再造轮子了,
    但是我觉得,这个轮子和其他轮子是不同的。
    这个轮子有别的轮子不具备的优势,
    或是与别的轮子各有所长。
      

  15.   

    一个项目完全可以全部用ObjectDataSource数据源控件+低层强类型的DataSet来开发!
    多表操作,存储过程,事务等都完全可以!
    比传统的代码方式大概减少50%的代码量吧!
    我仅仅从简单角度看,并不是否认楼主的追求!
      

  16.   

    待我仔仔细细看看强类型的DataSet
      

  17.   


    你也可以看看,Pool和强类型的DataSet比较一下,我想一定各有优势的
      

  18.   

    朋友,你确定"clazz"没有写错吗?
      

  19.   

    强类型的DataSet是图形方式,形成的后台代码是xml,
    就图形方式这点就比传统的代码方式写DAL强啊!
      

  20.   

    ???????
    1.级联删除是由业务决定的,不是由程序决定的2.其实你写实体表达业务模型,我认为有几点不合适
    a.重复写一遍(数据库表已经存在,其实表和实体是一个东西,一个存在在数据库,一个在内存中)
    b.Clazz和Student之间关系,不能反找,可以通过Student可以找到Clazz,但是你在查询中无法通过Clazz找到Student,有人说可以在Clazz增加一个 List<Student>属性,这样是可以的,但是你做了一个没有意思的工作,同时增加复杂性,遇到一种这样的情况就要做这种处理
    c.将数据转换为实体模型,需要消耗性能,所有hibernate有个问题就是性能问题
      

  21.   


    楼主,我改进了一下PDF.NET框架,下面是测试结果:19291104        优化过的ado.net
    22091263        一般的ado.net
    26241501        pool简化
    73604210        反射
    46932684        使用DateTable
    20331162        linq ms:
    20081148        pool查询
    15160868        pdf ms:
    ----------------------------
    现在PDF.NET 比LINQ还快,比你的pool查询也快,其实秘密就在这里:
     public static List<MyNameSpace.Table1> QueryList(System.Data.IDataReader reader)
            {
                List<MyNameSpace.Table1> list = new List<MyNameSpace.Table1>();
                using (reader)
                {                if (reader.Read())
                    {
                        int fcount = reader.FieldCount;
                        string[] names = new string[fcount];
                        object[] values = new object[fcount];                    for (int i = 0; i < fcount; i++)
                            names[i] = reader.GetName(i);                    do
                        {
                            reader.GetValues(values);                        MyNameSpace.Table1 t = new MyNameSpace.Table1();
                            t.PropertyNames = names;
                            t.PropertyValues = values;                        list.Add(t);
                        } while (reader.Read());                }
                }
                return list;
            }
      

  22.   

    最近正在学些这一类的东东,MARK一下,呵呵。
      

  23.   

    呵呵,故意的,class是关键字嘛,尽量避开
      

  24.   

    连级删除我也正在考虑是否提供,如果完全交给业务逻辑,关系复杂时,比较繁琐。这也不是我的注意,前辈们的经验啊,这样的话也可以享受强类型语言的工具带来的好处,点一下就可以出来提示。那个List爱用不用,并没有规定,外键关系也可以爱用不用,直接用ClassId做属性也可以。hibernate性能不佳不是因为实体,是因为他提供的强大功能,加上使用反射技术。不过着新能损失可以忽略不计(我不同意某些高手天天把性能挂在嘴边上)。
    而Pool的性能你大可放心,比一般的手写Ado.Net更快,因为Pool运行时动态编译,并且做过优化。
      

  25.   

    bluedoctor。
    呵呵,恭喜你拉。
    你这个办法确实很高,
    效率应该是可以提升了。
    但是访问实体的时候,
    仍然要面对缓慢的hash的。
    有些把效率转移开的意思。
      

  26.   

    现在已经从根本上抛弃了哈希表,这个太耗内存,另外,如果数据库字段主要是字符型,那么在取值的时候,很少会用到装箱操作,所以总体上,性能飞升!我准备升级 PDF.NET框架到 4.0,请大家期待!