利用抽象、多态实现无反射的绿色环保ORM框架
最近一直在忙新公司的基础库建设,对系统架构、开发框架及快速开发平台的设计实施都积累了一定的实践经验。
一般的中小型的软件开发公司,如果按照技术储备来衡量软件项目的技术含量的评定依据是可行的。但如果光是按照人头来衡量软件的技术含量是不可靠的。所以我们在选择跳巢的时候是选择大公司还是选择有技术含量的公司要根据自己的职业规划来。(本人最近体会到的一点跳巢经验分享给大家)
由于我现有单位技术部门刚刚成立不久,需要一些基础的开发框架,ORM当然是跑不了的。在后面的文章中我将陆续写下我在建设基础框架中的一些实践检验,里面可能包括对UI层的封装、基础控件的封装等等。我就废话少扯了,进入主题。
这篇文章的重点是无反射的ORM框架,为什么会有这样的想法?其实前不久群里的朋友就问了一些问题,他们在构建自己的ORM框架的时候频繁的在使用反射来编写功能。从跟他们的交流上来看他们似乎很喜欢使用反射来写功能,但是没有仔细的研究过ORM框架的作用是在系统架构的哪个位置,在对性能要求十分严格的情况下反射会有点无能为力。
反射的好处当然是毋庸置疑的,一些技术稍微好点的或者大牛们通常会用动态编译的技术来平滑的过度这个系统最重要的性能瓶颈点。我总觉的可以用高层的抽象和约定来解决这个ORM使用反射的问题。下面我们来分析一下通常ORM框架为什么需要用反射,反射的目的是什么。[王清培版权所有,转载请给出署名]
ORM中反射的目的是什么?
当然我们这里讨论的是最普通的问题也是必须的问题。
ORM框架的种类形态各异,不同的公司不同的ORM实现。其实目的是为了能有一套属于自己公司的开发框架,这不是技术所定而是公司高层领导所要求的。(我们没有说话的权利,为了保住饭碗,我们只能听从指挥)
但是大部分的ORM框架的设计思想和实现思路都离不开那几点的“思维实现约束”。我为什么要说“思维实现约束”,这也是我们程序员的一些通病之一吧,都喜欢用复杂的技术。不管三七二十一用了心里舒服。这是好事,为了练习技术可以理解。没有这份好奇心这份激情我们也很难走到专家的位置。
目的之一:为了表达实体与表的对应关系
ORM是实体与表的一种映射关系,逐渐被发展为一种复杂的技术实现模型。
在传统的分层架构中,在实体的定义上都会使用一个特性来标记该实体所表示的表名称是什么。如:
[Serializable()]
[HZ.Table(TableName = "member")]
public class Member{}
特性HZ.Table中的属性TableName来保存静态的表名,在ORM中通过获取对象的类型然后反射出该类型的特性元数据。然后读取相关成员属性值,作为拼接SQL语句的必备条件。
目的之二:为了表达属性与字段的对应关系及一些主、外键
ORM中将实体的属性映射成数据库中表的字段,一般通过两种方式来表达这中关系。
第一种:通过属性特性来表示该属性代表的字段名称;
[HZ.Column(PrimaryKey = true,ColumnName=”MemberId”)]
public string MemberCode { get; set; }
第二种:直接通过属性名称来表示字段的名称;
public string MemberId { get; set; }
目的之三:获取实体属性中的值
在进行插入或更新的时候需要获取实体中的属性的值,这个时候只能使用反射的方式获取到属性的值,然后拼接插入或更新语句。
目的之四:设置实体属性的值
通过实例化泛型对象,然后反射对象的属性通过SetValue方法设置属性的值。
简结:这几点是最常用的,可能还包括其他复杂的功能,这里我就不涉及了。上面这几点都是通过反射获取实体的信息,不管是增、删、改、查都需要反射。尤其是对于查询数据来说,如果是大数据量的查询性能问题很吓人。
通过抽象、多态设计不需要特性的ORM实体
大部分ORM框架是需要代码生成器做支持的,不是所有的代码都是需要程序员手动去敲的,可以通过一些模板引擎类的代码生成器,编辑好自己的模板然后生成大部分的实体代码。包括.NET里面的EntityFramework、LinqToSql也是用IDE集成的代码生成器。
所以这里就会涉及到对企业代码生成器的考虑,这里就先不扯了,后续文章我们再来针对性讨论。
那么我们先来讨论如何设计实体结构,让它能包含我们ORM所需要的必备信息。其实我们的思路稍微转变一下利用抽象来解决问题。提高抽象层次,将实体视为两个层面。顶层抽象类被ORM使用,子类被调用者使用。
图:
我们的要求就是ORM中不能存在一个反射的代码。所以我们约定了BasicEntityObject抽象类,通过定义顶层抽象基类来包含子类所要用到的一些属性信息。
我们看一下抽象类中包含了哪些东西。
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Collections;
- namespace Glory.Net.ORM
- {
- public abstract class BaseEntityObject : DictionaryBase
- {
- /// <summary>
- /// 实体对象对应数据库中的表名
- /// </summary>
- private string _tablename = string.Empty;
- /// <summary>
- /// 受保护字典:实体类中对应数据库表中主键的属性和属性类型
- /// </summary>
- protected Dictionary<string, string> _primarydictionary = new Dictionary<string, string>();
- /// <summary>
- /// 用于实体子类设置当前子类对应数据库中的表名
- /// </summary>
- protected string TableName
- {
- get { return _tablename; }
- set { _tablename = value; }
- }
- /// <summary>
- /// 客户代码获取当前实例对应ORM中的表名
- /// </summary>
- public string GetTableName
- {
- get { return _tablename; }
- }
- /// <summary>
- /// 用于实体子类设置当前实例的属性值
- /// </summary>
- /// <param name="key"></param>
- /// <returns></returns>
- protected object this[string key]
- {
- get { return this.Dictionary[key]; }
- set { this.Dictionary[key] = value; }
- }
- /// <summary>
- /// 设置实例的属性值
- /// </summary>
- /// <param name="key">属性名称</param>
- /// <param name="value">属性值</param>
- public void SetEntityVlues(string key, object value)
- {
- if (this.Dictionary.Contains(key))
- {
- this.Dictionary[key] = value;
- }
- }
- /// <summary>
- /// 获取实例的属性键值队
- /// </summary>
- /// <returns></returns>
- public IDictionary GetEntityValue
- {
- get{return (IDictionary)this.Dictionary;}
- }
- /// <summary>
- /// 获取实例的主键信息
- /// </summary>
- public IDictionary GetEntityPrimary
- {
- get { return (IDictionary)_primarydictionary; }
- }
- protected abstract void AddPrimaryToDictionary();
- }
- }
其实代码很简单,就是为了将子类的属性值保存到基类中来,让子类只是一个空壳子。
- public class Tb_Car : VluesInitTb_Car
- {
- /// <summary>
- /// 唯一主键
- /// </summary>
- public string CID
- {
- get { return this["CID"] as string; }
- set { this["CID"] = value; }
- }
- /// <summary>
- /// 车牌号
- /// </summary>
- public string CarBanrdCode
- {
- get { return this["CarBanrdCode"] as string; }
- set { this["CarBanrdCode"] = value; }
- }
- /// <summary>
- ///
- /// </summary>
- public string CarCode
- {
- get { return this["CarCode"] as string; }
- set { this["CarCode"] = value; }
- }
- /// <summary>
- ///
- /// </summary>
- public string DriverName
- {
- get { return this["DriverName"] as string; }
- set { this["DriverName"] = value; }
- }
- /// <summary>
- /// 联系电话
- /// </summary>
- public string Mobile
- {
- get { return this["Mobile"] as string; }
- set { this["Mobile"] = value; }
- }
- /// <summary>
- /// 车型
- /// </summary>
- public string CarType
- {
- get { return this["CarType"] as string; }
- set { this["CarType"] = value; }
- }
- /// <summary>
- /// 购车日期
- /// </summary>
- public DateTime? BuyDateTime
- {
- get { return this["BuyDateTime"] as DateTime?; }
- set { this["BuyDateTime"] = value; }
- }
- /// <summary>
- /// 所属中心编号。外键
- /// </summary>
- public string AttachCenter
- {
- get { return this["AttachCenter"] as string; }
- set { this["AttachCenter"] = value; }
- }
- /// <summary>
- /// 所属区部编号。外键
- /// </summary>
- public string AttachSection
- {
- get { return this["AttachSection"] as string; }
- set { this["AttachSection"] = value; }
- }
- /// <summary>
- /// 所属站点编号。外键
- /// </summary>
- public string AttachStop
- {
- get { return this["AttachStop"] as string; }
- set { this["AttachStop"] = value; }
- }
- }
那么中间的类是干嘛用的呢,是为了将初始化隔离在基类中;
- [Serializable()]
- public class VluesInitTb_Car : BaseEntityObject
- {
- public VluesInitTb_Car()
- {
- this.TableName = "Tb_Car";
- /// <summary>
- /// 唯一主键
- /// </summary>
- this.Dictionary.Add("CID", null);
- /// <summary>
- /// 车牌号
- /// </summary>
- this.Dictionary.Add("CarBanrdCode", null);
- /// <summary>
- ///
- /// </summary>
- this.Dictionary.Add("CarCode", null);
- /// <summary>
- ///
- /// </summary>
- this.Dictionary.Add("DriverName", null);
- /// <summary>
- /// 联系电话
- /// </summary>
- this.Dictionary.Add("Mobile", null);
- /// <summary>
- /// 车型
- /// </summary>
- this.Dictionary.Add("CarType", null);
- /// <summary>
- /// 购车日期
- /// </summary>
- this.Dictionary.Add("BuyDateTime", null);
- /// <summary>
- /// 所属中心编号。外键
- /// </summary>
- this.Dictionary.Add("AttachCenter", null);
- /// <summary>
- /// 所属区部编号。外键
- /// </summary>
- this.Dictionary.Add("AttachSection", null);
- /// <summary>
- /// 所属站点编号。外键
- /// </summary>
- this.Dictionary.Add("AttachStop", null);
- AddPrimaryToDictionary();
- }
- /// <summary>
- /// 实体类 重写实体基类添加主键信息方法,主键数据类型首字母要大写
- /// </summary>
- protected override void AddPrimaryToDictionary()
- {
- _primarydictionary.Add("CID", "string");
- }
- }
通过这种层次的抽象可以很好的规避特性带来的性能问题。在ORM中我们的泛型方法都是约束实体为BaseEntityObject类型,然后所有的信息包括主键、字段、数据类型都能够通过多态的方式获取到。
ORM通过实例化一个对象的实例然后将其缓存起来,作为后续使用。而不需要频繁的实例化中间对象带来的性能问题。
其实大部分的代码都是可以通过代码生成器生成的,我们也正在为公司开发符合自己公司产品的代码生成器,包括对业务代码的高度抽象、业务建模后的代码生成。
当然该篇文章只是简单的讲解了一下核心的内容,也算是抛砖引玉吧。希望对大家来说有点启发作用。[王清培版权所有,转载请给出署名]
本文出自 “pattern driven the world” 博客,请务必保留此出处http://wangqingpei557.blog.51cto.com/1009349/855135
利用抽象、多态实现无反射的绿色环保ORM框架的更多相关文章
- Winform打砖块游戏制作step by step第5节---重构代码,利用继承多态
一 引子 为了让更多的编程初学者,轻松愉快地掌握面向对象的思考方法,对象继承和多态的妙用,故推出此系列随笔,还望大家多多支持. 二 本节内容---重构代码,利用继承多态 1. 主界面截图如下: 2. ...
- nodejs利用ajax实现网页无刷新上传图片
nodejs利用ajax实现网页无刷新上传图片 标签(空格分隔): nodejs 通常情况下上传图片是要通过提交form表单来实现的,但是这又不可避免的产生了网页转. 利用ajax技术和FormDat ...
- C# 通过反射初探ORM框架的实现原理
背景: 以前学的Java进行开发,多用到Mybatis,Hiberante等ORM框架,最近需要上手一个C#的项目,由于不是特别难,也不想再去学习C#的ORM框架,所以就想着用反射简单的实现一下ORM ...
- 手机也需“绿色环保”,省电类APP或将成为“标配”?
随着移动APP的大幅添加.非常多用户发现,这手机耗电量是越来越大了,在各种娱乐应用.办公应用.社交应用的冲击下,以往两天充一次电都OK.如今一天充一次还不够用,因为续航能力变弱.这也为用户带来 ...
- ORM框架通过映射(反射)获取数据库的数据
ORM(Object Relational Mapping)框架采用元数据来描述对象一关系映射细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中.只要提供了持久化类与表的映射关系,ORM ...
- C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)
反射以及Attribute在ORM中的应用 一. 反射什么是反射?简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等.反射有什么用呢?反射不但让你在运行 ...
- C#反射の一个泛型反射实现的网络请求框架
点击下载源码 C#反射の反射详解(点击跳转)C#反射の反射接口(点击跳转)C#反射反射泛型接口(点击跳转)C#反射の一个泛型反射实现的网络请求框架(点击跳转)
- K2助力大鹏绿色环保之路
K2和大鹏结缘可以追溯到2006年,两家践行绿色环保理念的公司一拍即合,开始了十余年的合作之路,在“绿色环保”的路上留下了坚实的脚印. 绿色能源:LNG 广东大鹏液化天然气有限公司成立于2004年 ...
- C#根据反射和特性实现ORM映射实例分析
本文实例讲述了C#根据反射和特性实现ORM 映射的方法.分享给大家供大家参考.具体如下: (一)关于反射 什么是反射? 反射就是在运行时,动态获取对象信息的方法.比如:运行时获得对象有哪些属性,方法, ...
随机推荐
- 【HDU3440】House Man (差分约束)
题目: Description In Fuzhou, there is a crazy super man. He can’t fly, but he could jump from housetop ...
- FLASH 存储学习-串行SPI NOR FLASH
1.1 SST25VF080B简介1.1.1 主要特性 关键点:容量.速度(时钟速度.读写速度).功耗. l 容量:8MBit: l 最高SPI时钟频率:50MHz: l 低功耗模式下电流消耗:5uA ...
- Android仿微信SlideView聊天列表滑动删除效果
package com.ryg.slideview; import com.ryg.slideview.MainActivity.MessageItem; //Download by http://w ...
- WordPress Event Easy Calendar插件多个跨站请求伪造漏洞
漏洞名称: WordPress Event Easy Calendar插件多个跨站请求伪造漏洞 CNNVD编号: CNNVD-201309-083 发布时间: 2013-09-11 更新时间: 201 ...
- 【原】Centos6.5下cdh4.6 hive安装部署
1.前提条件: 只需要选择一台服务器即可,这里选择安装在namenode上:安装用户为cloud-user 2.安装包: sudo yum install -y hive hive ...
- lightoj1051 Good and Bad (dp)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1051 题目大意:给你一个字符串,只包含大写字母和‘?’,如果字符串中出现了连续三个以上 ...
- linux-kernel/CodingStyle
https://www.kernel.org/doc/Documentation/zh_CN/CodingStyle Chinese translated version of Documentati ...
- c++ 流基本概念
前言 在程序经常实现数据的输入和输出,c++通过一种称为流(stream)的机制提供了更为精良的输入和输出方法.流是一种灵活且面向对象的I/o方法. 根据操作对象不同分为文件流.字符串流.控制台流. ...
- 【设计模式 - 12】之代理模式(Proxy)
1 模式简介 1.1 定义 为其他对象提供一种代理以控制对这个对象的访问.代理对象起到中介作用,可以去掉功能服务或增加额外服务. 1.2 常见的代理模式 1) 远程 ...
- NDK安装 eclipse 不出现NDK目录问题
android adt自带eclipse无法设置ndk路径 具体情况是 我在mac上搭建android环境 到android sdk官网下载r23版本的adt时自带的eclipse没有设置ndk路径的 ...