C#基础系列:实现自己的ORM(构造我自己的ORM)
【转 http://blog.csdn.net/ronotian/article/details/2900734】
构造我自己的ORM
通过前面两章的描述,我相信很多朋友都已经明白我了下面将要讨论到的ORM的实现方法了,那就是根据自定义Attribute来定义O/R Mapping规则,然后通过反射来动态获取此规则,动态构造SQL语句。 由于这个小东西(ORM)出生在深圳,所以我想来想去,她应该有个深圳的名字,所以我就叫她“MiniORM”。不知道各位认为如何? MiniORM采用的是ONE_INHERIT_TREE_ONE_CLASS(一个继承树对应于一个表)的结构,虽然这种结构容易导致数据冗余,但是这种结构很简单。另,本MiniORM 仅仅考虑一个表一个PK,一个FK的情况。
MiniORM结构如下,为了更便于理解和使用,我使用了3个类: 1、OrmWriter:负责将实体对象(比如前面章节说的Person)插入数据库和修改数据库中对应的记录。 2、OrmRemover:负责根据实体对象,删除指定的记录; 3、OrmReader:负责根据实体对象,读取指定的记录;
上面就是MiniORM的3个主要类。下面我们就详细地根据前面的描述一步步构造她。我们这里还是以前面说的Person为例进行说明。
通过本系列第一章,我们知道,对象不但存在继承关系,特别在实际的应用中还存在包含关系,比如一个Person包含两个Hand(手)类,包含一个Head(头)类等,我们的Person在数据库中应该有一个ID,为了更加方便使用和讨论,此ID在MiniORM中是一个int以及自动增长类型(ID INDENTITY(1,1))。这些都是我们的MiniORM应该考虑的范围。 我们对我们的Person做修改:
- [DataObjectAttribute("Person")]
- publicclass Person
- {
- privateint _ID;
- privatestring _Name;
- privateint _Age;
- privatestring _Sex;
- private Head _Head;
- private Hand _LeftHand;
- private Hand _RightHand;
- publicint ID
- {
- get { return _ID; }
- set { _ID = value; }
- }
- public Head Head
- {
- get { return _Head; }
- set { _Head = value; }
- }
- public Hand LeftHand
- {
- get { return _LeftHand; }
- set { _LeftHand = value; }
- }
- public Hand RightHand
- {
- get { return _RightHand; }
- set { _RightHand = value; }
- }
- [DataFieldAttribute("name", "NvarChar")]
- publicstring Name
- {
- get { returnthis._Name; }
- set { this._Name = value; }
- }
- [DataFieldAttribute("age", "int")]
- publicint Age
- {
- get { returnthis._Age; }
- set { this._Age = value; }
- }
- [DataFieldAttribute("sex", "NvarChar")]
- publicstring Sex
- {
- get { returnthis._Sex; }
- set { this._Sex = value; }
- }
- }
你可能又发现了一个问题,就是在我们修改后的Person中,增加了LeftHand,RightHand以及Head,可是这三个都属于类啊,这个怎么能够保存到数据库中呢?并且使用我们前面的DataFieldAttribute是没有办法描述的啊。另外还增加了个ID,又怎么来标志这个是自动增长的int型PK呢?当然了能够到这里你就发现这些问题那是相当的不错了。如果前面就动手的人,估计考虑的还是修改我们的DataFieldAttribute让它能够对这些东西进行区别。比如在DataFieldAttribute中再增加一个属性用于区别哪个是ID属性,哪个是对象类型(比如Hand)属性。这当然是好的,只不过这样做导致我们的代码极其丑陋。最好的办法还是另外增加一个Attribute。当然了,我是为了更加方便的构造SQL语句,我做的不是很好。 1、怎么表示实体类对应的数据库表的PK和FK? 为了更方便的实现,MiniORM中标志一个实体类的PK和FK都是在DataObjectAttribute中来做(其实最好的办法还是另外增加个比如PKAttribute和FKAttribute,不过这个留给其它人去做吧)。如下,DataObjectAttribute第一个参数表示对应的数据库表,第二个参数表示PK,第三个参数表示FK:
- [DataObjectAttribute("Person", "ID", "")]
- publicclass Person
- {
- ......
- }
2、怎么标志字段是Indentity(自动增长)? 在DataFieldAttribute中增加了个属性,用于标志某个字段是否自动增长的字段。这些都是我个人懒做的,其中,第二个参数标志ID是Identity类型
- [DataFieldAttribute("ID", true)]
- publicint ID
- {
- get { return _ID; }
- set { _ID = value; }
- }
3、怎样标志字段是类对象(比如Person中的Hand,当然复杂点的对象,可能包含子对象列表)? 由于MiniORM提供的是一个类似框架的东西,所以不应该受到实体类的限制,所以对于类对象字段,我们应该能够描述此对象所在的程序集,命名空间,类名,这样我们才可以运行时创建该对象。
- publicclass SubDataObjectAttribute : Attribute
- {
- private SubDataObjectFieldType _FieldType;
- privatestring _AssemblyName;
- privatestring _NamespaceName;
- privatestring _ClassName;
- public SubDataObjectAttribute(SubDataObjectFieldType fieldtype, string assemblyname, string namespacename, string classname)
- {
- this._FieldType = fieldtype;
- this._AssemblyName = assemblyname;
- this._NamespaceName = namespacename;
- this._ClassName = classname;
- }
- /// <summary>
- /// 本记录对应的FieldType
- /// </summary>
- public SubDataObjectFieldType FieldType
- {
- get { return _FieldType; }
- }
- /// <summary>
- /// 本记录对应的AssemblyName
- /// </summary>
- publicstring AssemblyName
- {
- get { return _AssemblyName; }
- }
- /// <summary>
- /// 本记录对应的NamespaceName
- /// </summary>
- publicstring NamespaceName
- {
- get { return _NamespaceName; }
- }
- /// <summary>
- /// 本记录对应的ClassName
- /// </summary>
- publicstring ClassName
- {
- get { return _ClassName; }
- }
- }
其中SubDataObjectFieldType是一个枚举类型,因为我们的子对象可能是单独的对象比如Person.Head也可能是一个列表(List)。所以我增加了这个枚举类型,用于做标志。
- publicenum SubDataObjectFieldType
- {
- Object,
- /// <summary>
- /// 本字段属于List类型,直接遍历
- /// </summary>
- List,
- }
当然了,这里的子对象列表可能是ArrayList,HashTable等等,你都可以根据自己项目中实际使用到的类型来做相应的修改。
4、怎么控制某个字段在表中不能重复? 比如我们要控制Person.Name不能重复,如果你新增的时候发现重复要提示。那我们也通过增加一个Attribute的形式来实现。这个Attribute很简单,没有任何方法和属性。
- publicclass DataFieldNotDoubleAttribute : Attribute
- {
- }
5、怎样做事务处理? 事务处理是每个底层框架都应该考虑到的问题,在.NET中我们有两种方式来进行事务处理,一种是使用COM+,这是最好的方法,不过性能上比较欠缺,另外这东西配置很麻烦,当你数据库安装在另外一太服务器上的时候,往往出现无法使用的问题,我曾经就被这东西折腾够呛,所以我干脆就不用他了,不过还是介绍下语法,通过使用TransactionScope就可以很好的使用com+提供的事务处理,代码相当的简洁,优美,只可惜啊!天使的面孔,魔鬼的心。
- publicvoid function1()
- {
- using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
- {
- function2();
- }
- }
- publicvoid function2()
- {
- using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
- {
- //DoSomething();
- }
- }
另外一种方法就是使用SqlTransaction:
- using (SqlConnection conn = new SqlConnection(ConnectionStr))
- {
- conn.Open();
- SqlTransaction trans = conn.BeginTransaction();
- //DoSomething();
- trans.Commit();
- }
不过遗憾的是这种方式不能实现事务嵌套,所以只能通过将trans作为参数进行传递来实现事务处理。
经过上面一系列的修改后,我们的Person成了什么样子了?
- [DataObjectAttribute("Person", "ID", "")]
- publicclass Person
- {
- privateint _ID;
- privatestring _Name;
- privateint _Age;
- privatestring _Sex;
- private Head _Head;
- private Hand _LeftHand;
- private Hand _RightHand;
- [DataFieldAttribute("ID", true)]
- publicint ID
- {
- get { return _ID; }
- set { _ID = value; }
- }
- [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Head")]
- public Head Head
- {
- get { return _Head; }
- set { _Head = value; }
- }
- [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
- public Hand LeftHand
- {
- get { return _LeftHand; }
- set { _LeftHand = value; }
- }
- [SubDataObjectAttribute(SubDataObjectFieldType.Object, "Person", "Person", "Hand")]
- public Hand RightHand
- {
- get { return _RightHand; }
- set { _RightHand = value; }
- }
- [DataFieldAttribute("name", "NvarChar")]
- publicstring Name
- {
- get { returnthis._Name; }
- set { this._Name = value; }
- }
- [DataFieldAttribute("age", "int")]
- publicint Age
- {
- get { returnthis._Age; }
- set { this._Age = value; }
- }
- [DataFieldAttribute("sex", "NvarChar")]
- publicstring Sex
- {
- get { returnthis._Sex; }
- set { this._Sex = value; }
- }
- }
当然了对于Person这样的实体类,我们完全可以自己写代码自动生成工具来弄,然后再做很小的修改就可以了,这样的工具实现简单,我就不讨论了。 好了,关于我的MiniORM我就讨论到这里了,其它的请看代码吧。
ORM虽然是好东西,但是也存在很多方面的不足,首先我们能够做到的是将大部分的数据库操作交个ORM来做。另外少部分还是需要我们自己写SQL的。单大部分的工作的分离可以为我们节约大量的时间(也就是所谓的20/80原则,80%的教给ORM来处理,20%的自己做,当然很好了)。另外通过将这些相同的流程教给ORM来处理,可以避免很多的疏忽导致的失误(比如不小心把某个Insert,Update,Delete语句弄错了什么的)。 最主要的缺点当然是性能问题,特别是我的MiniORM,全部采用反射来获取映射规则,从而导致性能上更多的下降,不过我们了解方法以后是很容易通过动态生成代码,动态编译的方式来减少这部分的性能损失的。另外某些部分的代码显得有些臃肿,特别是把判断是否Indentity这样的代码放DataFieldAttribute中来处理(这个完全可以象DataFieldNotDoubleAttribute一分开处理的样)等等。
转载于:https://www.cnblogs.com/chen20090616/archive/2013/01/11/2856764.html
C#基础系列:实现自己的ORM(构造我自己的ORM)相关推荐
- C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)
反射以及Attribute在ORM中的应用 一. 反射 什么是反射? 简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等. 反射有什么用呢? 反射不但让 ...
- C#基础系列2:反射以及Attri…
原文地址:C#基础系列2:反射以及Attribute在ORM中的应用[转] 作者:YouthWithYouth 反射以及Attribute在ORM中的应用 一. 反射 什么是反射? 简单点吧,反射就是 ...
- 【C++自我精讲】基础系列二 const
[C++自我精讲]基础系列二 const 0 前言 分三部分:const用法.const和#define比较.const作用. 1 const用法 const常量:const可以用来定义常量,不可改变 ...
- java代码讲解_Java基础系列-代码块详解
注:本文出自博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 前言 Java基础系列,尽量采用通俗易懂.循序渐进的方式,让大家真正理解Java基础知识! 代码块 ...
- 【JavaScript基础系列】决定你的人生能走多远的,是基础。
前言 javaScript门槛非常低,一点语法,一个dom,一个bom就可以使用它开发大部分js应用,再加上现在层出不穷的框架极大的简化抽象了javaScript的使用方式,但是我们始终不能忘记的一点 ...
- java中this_夯实Java基础系列7:一文读懂Java 代码块和执行顺序
目录 #java中的构造方法 #构造方法简介 #构造方法实例 #例-1 #例-2 #java中的几种构造方法详解 #普通构造方法 #默认构造方法 #重载构造方法 #java子类构造方法调用父类构造方法 ...
- C#基础系列——Attribute特性使用
前言:上篇 C#基础系列--反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术--特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属 ...
- 求python一个类与对象的代码_Python基础系列(五)类和对象,让你更懂你的python代码...
首先,非常感谢各位打开本博文,本博文是Python入门基础系列文章之一,Python目前是非常火的编程工具,其实作为编程不在只是程序员的专利,每一个人在日常的工作中.学习中都会或多或少的要用到一些工具 ...
- [转载] 夯实Java基础系列8:深入理解Java内部类及其实现原理
参考链接: Java内部类 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tut ...
- Java面试题基础系列
Java面试题基础系列 1.面向对象的特征有哪些方面? 面向对象的特征主要有以下几个方面:抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面.抽象只关注对象有哪些属性和 ...
最新文章
- 未来趋势?通过无线技术管理汽车电池,可消除90%物理布线
- Spring Boot实现热部署
- 二分大法| 求X的开方,结果一个公式解决! (力扣69.X 的平方根)
- SAP S/4HANA生产订单状态含义
- Java程序中AB类可调用_19年【石油大学】《Java语言程序设计》二次在线作业(100分)...
- linux nginx 重启_Nginx 的介绍及安装
- 12C RAC for ASM添加磁盘步骤
- Linux的实际操作:用户管理(删除用户userdel userdel -r)
- 今天项目中遇到一个关于DataGrid的 ItemDataBound自动判断下拉列表默认值绑定问题...
- mongoose在子文档的array里update或insert
- linux之添加python环境变量
- c语言中线程的特性,具有C 11多线程的特征库
- Flutter 高级篇-所有知识点架构
- C# 判断输入的字符是不是数字
- 行翻转和列翻转_用量子计算机翻转硬币
- 触摸世界:10年前我写下这些诗歌,10年后我也不讨厌它们
- PT100铂电阻温度传感器详解
- java韩信点兵_韩信点兵练习题(死循环的应用)
- 使用RT-Thread Studio DIY 迷你桌面时钟(二)| 获取温湿度传感器数据(I2C设备驱动+SHT3x软件包)
- Quartz.NET总结(五)基于Quartz.net 的开源任务管理平台
热门文章
- ModBus TCP/IP协议
- 4.2创建自定义Spring Boot自动配置Starter
- 【渝粤教育】国家开放大学2018年春季 0032-21T农业经济学 参考试题
- [渝粤教育] 西南科技大学 经济法学 在线考试复习资料(1)
- [渝粤教育] 西南科技大学 单片机原理与应用 在线考试复习资料
- C++并发与多线程(五)互斥量,atomic、与线程池
- 23种设计模式(二十三)行为变化之访问器
- Docker系列(五)实战:在容器中部署静态网站
- 【5分钟 Paper】Deterministic Policy Gradient Algorithms
- HTML--day01