关于orm框架设计,还有必要说的或许就是Model解析了,也是重要的一个环节,在实现上还是相对比较简单的.

  Model解析,主要用到的技术是反射了,即:把类的属性与表的字段做映射. 把自己的设计及实现思路写出来也希望能有人给很好的优化建议,同时也给新手一点启发吧.

  首先先给Model属性定义特性,先普及一下"特性"的概念和为什么用特性(Attribute).

  简单来说,特性是给一个类,或方法,或属性 打上一个标记(或者叫附加信息),具体理解还是看例子比较好吧,

  在做类与表之间映射时,我们需要知道某字段的是什么类型,长度,是否主键,自增长,非空,等信息,最简单较直观的方法或许就是特性(Attribute)了,

首先我们定义一个特性,它就是一个类而已,它必须继承自Attribute,我所写的orm比较轻量级,仅几个比较关键属性,

代码如下:

 
public class ModelAttribute : Attribute
{
/// <summary>
/// 是否主键
/// </summary>
public bool IsPrimaryKey { set; get; }
/// <summary>
/// 主键是否自动增长
/// </summary>
public bool IsIdentity { set; get; }
/// <summary>
/// 是否非空字段
/// </summary>
public bool IsNotNull { set; get; }
/// <summary>
/// 列名
/// </summary>
public string ColumnName { set; get; }
}

下面是一个实体类使用特性的例子,它指明了Id的列名是:"Id",不允许为空的,是自增长的,是主键:

public class Test1 : ModelBase
{
[ModelAttribute(IsPrimaryKey = true, IsIdentity = true, IsNotNull = false, ColumnName = "Id")]
public int Id { set; get; }
public string Name { set; get; }
public string Age { set; get; }
public string Remark { set; get; }
}

下面是通过反射把Model特性解析出来,先把核心代码贴出来:

     /// <summary>
/// 通过解析获得Model的对象的参数,Key:为类的属性名
/// </summary>
/// <param name="model">model对象</param>
/// <returns>返回model参数</returns>
protected override Dictionary<string, ModelAttribute> GetModelParam<TModel>()
{
var list = new Dictionary<string, ModelAttribute>();
PropertyInfo[] pros = ReflectionHelper.GetPropertyInfo<TModel>();
foreach (PropertyInfo item in pros)
{
var attr = ReflectionHelper.GetCustomAttribute<ModelAttribute>(item);
if (attr == null)
{
//如果实体没定义属性则创建一个新的
attr = new ModelAttribute();
attr.ColumnName = item.Name;
}
else
{
//如果列名没有赋值,则将列名定义和属性名一样的值
if (string.IsNullOrEmpty(attr.ColumnName))
{
attr.ColumnName = item.Name;
}
}
list.Add(item.Name, attr);
}
return list;
}

因考虑反射应该是共同方法,不仅限于Model解析,所以把反射相关的方法提出来了,以下是根据"类型T"获取自定义属性的两个方法:

        /// <summary>
/// 获得指定成员的特性对象
/// </summary>
/// <typeparam name="T">要获取属性的类型</typeparam>
/// <param name="pInfo">属性原型</param>
/// <returns>返回T对象</returns>
public static T GetCustomAttribute<T>(PropertyInfo pInfo) where T : Attribute, new()
{
Type attributeType = typeof(T);
Attribute attrObj = Attribute.GetCustomAttribute(pInfo, attributeType);
T rAttrObj = attrObj as T;
return rAttrObj;
}
        /// <summary>
/// 获得对象的所有公共属性信息
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="obj">获得的对象</param>
/// <returns>返回属性信息</returns>
public static PropertyInfo[] GetPropertyInfo<T>() where T : class
{
Type t = typeof(T);
PropertyInfo[] proInfo = t.GetProperties();
return proInfo;
}

解析特性我们不需要知道该类的具体实例,所以这里用了泛型,只需要知道Model类型即可,我的框架仅限于类的属性,这里只获取属性的"特性对象".

返回类型Dictionary<string,ModelAttribute> Key:为属性名,ModelAttribute 对象,

到这里解析的实现其实就完成,后面我又做了一些优化,我们想到反射时通常会联想到效率问题,而且既然是解析一个类的特性,那么我们并不关心它的实例对象,

这里把解析出来的对象放到了缓存,即:只有第一次对该类进行反射,以后都是直接访问缓存数据.

解析Model是一个类,那么需要做到全局缓存,我这里用到了一个静态变量,该变量是不允许被外部更改的,所以设置为私有的了.

代码如下:

     static object _LockObj1 = new object();
static object _LockObj2 = new object(); /// <summary>
/// 实体类缓存,静态变量是保存为了减少反射次数
/// </summary>
static Dictionary<Type, Dictionary<string, ModelAttribute>> _ModelAttributeCache;
/// <summary>
/// 实体类缓存,静态变量是保存为了减少反射次数
/// </summary>
protected Dictionary<Type, Dictionary<string, ModelAttribute>> ModelAttributeCache
{
get
{
if (_ModelAttributeCache == null)
{
lock (_LockObj1)
{
if (_ModelAttributeCache == null)
{
_ModelAttributeCache = new Dictionary<Type, Dictionary<string, ModelAttribute>>();
}
}
}
return _ModelAttributeCache;
}
}
/// <summary>
/// 获取Model的属性对象,获取第一次后会放入一个缓存列表中
/// 即只反射一次
/// </summary>
public Dictionary<string, ModelAttribute> GetModelAttribute<T>() where T : ModelBase, new()
{
Type t = typeof(T);
if (!ModelAttributeCache.ContainsKey(t))
{
lock (_LockObj2)
{
if (!ModelAttributeCache.ContainsKey(t))
{
var attrs = GetModelParam<T>();
ModelAttributeCache.Add(t, attrs);
}
}
}
return ModelAttributeCache[t];
}

这里缓存列表为: Dictionary<Type, Dictionary<string, ModelAttribute>> ,Type即Model类的类型.

解释一下加LockObj的意义,

我先声明一下,这个orm框架虽然比较轻量级,但我也不是共享的一个设计阶段或者或测试阶段的代码,也是经过几个小项目使用磨合过的.

_LockObj 是在一次多线程操作时发现的bug,当多个线程访问一个"全局对象"时,不加锁会访问冲突的问题.

Model解析类的路径:ZhCun.Framework.Common.Models.TableModel

下载了代码的可以去看下具体实现的详细方法.

在设计DalBase  时考虑了它应依赖抽象的理念,虽然没有想好关于Model解析除了反射还是否会有其它方法,但还是把它定义成了抽象.

到这已经完成了Model解析的功能.会再生成sql语句的时候用到它.

有了以下方法示例,估计sql文的生成就能实现了吧.

       //得到Model对象(第一次会反射,再次调用时是从缓存获取)
Dictionary<string, ModelAttribute> modelAttr = _ModelAnaly.GetModelAttribute<T>();
//key:字段名(属性名)
foreach (string item in modelAttr.Keys)
{
//得到列名(如果特性没有指定ColumnName值,则与属性名一样)
string colName = modelAttr[item].ColumnName;
//是否字增长
bool isIdentity = modelAttr[item].IsIdentity;
//是否主键
bool isPrimaryKey = modelAttr[item].IsPrimaryKey;
}

关于Model解析类的实现 相对设计来说比较简单.

如果有大神有啥好的建议,或有什么不足,希望能 探讨,指正 .

c# 轻量级 ORM 框架 之 Model解析 (四)的更多相关文章

  1. 轻量级ORM框架初探-Dapper与PetaPoco的基本使用

    一.EntityFramework EF是传统的ORM框架,也是一个比较重量级的ORM框架.这里仍然使用EF的原因在于为了突出轻量级ORM框架的性能,所谓有对比才有更优的选择. 1.1 准备一张数据库 ...

  2. c# 轻量级ORM框架 实现(一)

    发布一个自己写的一个轻量级ORM框架,本框架设计期初基于三层架构.所以从命名上来看,了解三层的朋友会很好理解. 设计该框架的目的:不想重复的写增删改查,把精力放到功能实现上. 发布改框架的原因:希望给 ...

  3. 轻量级ORM框架 QX_Frame.Bantina(二、框架使用方式介绍)

    轻量级ORM框架QX_Frame.Bantina系列讲解(开源) 一.框架简介 http://www.cnblogs.com/qixiaoyizhan/p/7417467.html 二.框架使用方式介 ...

  4. 轻量级ORM框架 QX_Frame.Bantina(一、框架简介)

    轻量级ORM框架QX_Frame.Bantina系列讲解(开源) 一.框架简介 http://www.cnblogs.com/qixiaoyizhan/p/7417467.html 二.框架使用方式介 ...

  5. .NET轻量级ORM框架Dapper入门精通

    一.课程介绍 本次分享课程包含两个部分<.NET轻量级ORM框架Dapper修炼手册>和<.NET轻量级ORM框架Dapper葵花宝典>,阿笨将带领大家一起领略轻量级ORM框架 ...

  6. 轻量级ORM框架Dapper应用一:Dapper安装

    一.Dapper简介 Dapper是一款轻量级ORM框架,为解决网站访问流量极高而产生的性能问题而构造,主要通过执行TSQL表达式而实现数据库的CQRS. 如果你在项目中遇到性能访问问题,选择Dapp ...

  7. 分享自己写的基于Dapper的轻量级ORM框架~

    1.说明 本项目是一个使用.NET Standard 2.0开发的,基于 Dapper 的轻量级 ORM 框架,包含基本的CRUD以及根据表达式进行一些操作的方法,目前只针对单表,不包含多表连接操作. ...

  8. C# 性能优化 之 秒表 Stopwatch。 Dapper一个和petapoco差不多的轻量级ORM框架

    Sweet小马 小马同学的编程日记. C# 性能优化 之 秒表 Stopwatch. 生词解释:Diagnostics[,daɪəg'nɑstɪks] n.诊断学 using System.Diagn ...

  9. Android轻量级ORM框架ActiveAndroid入门教程(转)

    注:没有找到出处,如有侵犯,请告知 开始ActiveAndroid神奇之旅: 在AndroidManifest.xml中我们需要添加这两个 AA_DB_NAME (数据库名称,这个name不能改,但是 ...

随机推荐

  1. sessionFactory.getCurrentSession()的引出

    当业务逻辑中需要开启事务执行,业务逻辑也要调用底层操作数据库的函数,那函数也要开启事务操作. 如果用sessionFactory.openSession()的话会引起处理不在同一个事务中,会造成出错. ...

  2. VC++2010配置使用MySQL5.6

    0.前提 安装后的文件概览 编译器:  VC++2010 MySQL版本:MySQL5.6.19 for win64 Connector版本:connector  c++  1.1.3 在VS2010 ...

  3. MapReduce 中job.setJarByClass()方法的疑惑

    在调试mr实例的时候,遇到如下的情况,如图所示 说明:就是我的mr程序类名称和我设置的setJarByclass()中设置的不一样,但是程序竟然没有报错!!!!当时把我吓尿了 疑惑:如果这样设置的话, ...

  4. 关于C++虚函数的一些东西

    先上概念,C++的多态性:系统在运行时根据对象类型,来确定调用哪个重载的成员函数的能力. 多态性是通过虚函数实现的.成员函数之前加了virtual,即成为虚函数. 有虚成员函数的类,编译器在其每个对象 ...

  5. mybatis源码学习: 编译的方法

    mybatis3用了一段时间,抽出时间来研究一下.具体用法参考官方文档就行,源码在这里.mybatis相对而言,规模较小,可以从中学习如何编写高质量的java项目. mybatis3使用maven管理 ...

  6. ubuntu 下安装sh 文件

    1. cd 到 指定文件夹 如: cd /home/ddy/下载 2. sudo chmod +x *.sh 3. sudo ./*.sh ok 了 (1)数据预处理    可以用下载好的数据集,也可 ...

  7. 60个响应式的Web设计教程–能够手机访问!

    想要学习响应式[responsive:屏幕自适应的效果]的网页设计和开发技术?在这个超大的收藏集合中,我想你定会找到想要开始学习的响应式网页设计教程. 面对超过1亿的手机互联网用户,开发专业和用户友好 ...

  8. Linux下如何进行FTP设置

    一.Redhat/CentOS安装vsftp软件 1.更新yum源 首先需要更新系统的yum源,便捷工具下载地址:http://help.aliyun.com/manual?spm=0.0.0.0.z ...

  9. PHP上传大文件和处理大数据

    1. 上传大文件 /* 以1.5M/秒的速度写入文件,防止一次过写入文件过大导致服务器出错(chy/20150327) */ $is_large_file = false; if( strlen($x ...

  10. 60分钟内从零起步驾驭Hive实战学习笔记

    本博文的主要内容是: 1. Hive本质解析 2. Hive安装实战 3. 使用Hive操作搜索引擎数据实战 SparkSQL前身是Shark,Shark强烈依赖于Hive.Spark原来没有做SQL ...