在.Net 3.5刚出来的时候,笔者对linq及其兴奋,当时准备做saas,需要将同名数据表根据用户的不同进行映射,所以对linq很是钻研了一翻,但后来性能方面有些不满意,还是改回了ado,但是ado下sql的调试一旦用过了linq,那显然太难接受了,所以后来干脆自己写了个类来模仿linq封装ado,当然因为没有编译器的支持,那肯定差得太远,但算是为orm打了个基础吧。
orm简单的说就是将OOP中将所使用的对象(包括实体和关系)直接和数据库中的数据表勾连起来。说得更直白一点就是把数据(数据库层面)和对象(OOP层面)实现无缝映射。类和数据表的勾连利用反射实现起来已经很简单了:
1 用扩展属性(继承自Attribute)对类中的属性进行标记;
2 在系统初始化时,对类的属性利用反射进行逐一检查,收集标记信息以及属性相关的各种信息;
3 对增删改查命令中,利用收集到的信息构造相应的sql语句;
4 对从数据库读出的数据,利用收集到的信息进行类型转换,赋值给对象的相应属性。
麻烦的有以下几点:
1 和表达式的结合,表达式的灵活加上C#的强类型检查对ORM来说实在是太必须了,如果不能支持用表达式进行条件查询,那orm基本上就没必要做了;
2 OOP中的数据类型和数据库中的数据类型的无缝转换,包括从OOP到数据库以及从数据库到OOP两个方向的转换,开始当然只需要考虑非结构化的简单数据类型了,大部分的简单数据类型一般在OOP和数据库间都有对应,这比较好,麻烦点的是bool和xml,最头痛的是枚举类型,要进行单独的识别与处理,还需要对基类型进行限定才能解决;
3 事务的处理,就事务本身来说,还不是很复杂,写一个事务对象专门封装数据库的事务也就是了,但麻烦的是事务执行过程中如果出现异常被撤销就问题大了:既然要实现ORM那肯定要提供缓存来减少数据库读操作,可如果对象被修改了但事务最终却被撤销了,那缓存中的对象和数据库中的数据就不一致了,所以还得记录参与到事务中的对象,事务撤销时再逐一从缓存中清除;但关系也有缓存,而关系又存在于两对象之间,只清自己很不行还得清和自己有关系的另一个对象;
4 父表的关联,OOP的最大优势就是依托于继承的代码复用,所以ORM也要能支持实体对象间的继承,也就是要将子类所对应的数据表和父类、祖类的数据表自动关联起来,增删改要能同步到父表,查要能将所有数据全部正确读出,并自动读取父类的相关信息进行正确的恢复,更关键的是,所有这些必须无缝、透明的予以实现;
5 最后还有一个小问题需要注意,实体类的ID一般需要设置为序列增长,那么就需要注意向数据库中插入时要避开该列,同时还要在插入后将新的ID值带回,如果执行插入的对象是继承自其它实体类的,更是要将新值逐级带回。