c# 轻量级 ORM 框架 之 Model解析 (四)

  关于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解析 (四)

时间: 2024-08-01 10:40:58

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

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

注:没有找到出处,如有侵犯,请告知 开始ActiveAndroid神奇之旅: 在AndroidManifest.xml中我们需要添加这两个 AA_DB_NAME (数据库名称,这个name不能改,但是是可选的,如果不写的话 是默认的"Application.db"这个值) AA_DB_VERSION (数据库版本号,同样是可选的 – 默认为1) <manifest ...> <application android:name="com.activeandro

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

发布一个自己写的一个轻量级ORM框架,本框架设计期初基于三层架构.所以从命名上来看,了解三层的朋友会很好理解. 设计该框架的目的:不想重复的写增删改查,把精力放到功能实现上. 发布改框架的原因:希望给初学者一个参考,希望能给予好的建议,给自己一个展示机会. 在我开始之前,先说明一下,我对"软件工程学"概念东西几乎不通,最高文化程度:初二,所以不喜勿喷. 开始我的orm设计最底层 最底层的是一个DalBase,它是一个抽象的,实现了增删改查的基本操作. 它既然是一个抽象的,那么它的内部就

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

一.EntityFramework EF是传统的ORM框架,也是一个比较重量级的ORM框架.这里仍然使用EF的原因在于为了突出轻量级ORM框架的性能,所谓有对比才有更优的选择. 1.1 准备一张数据库表 (1)For MSSQL CREATE TABLE [dbo].[Posts] ( [Id] INT NOT NULL PRIMARY KEY IDENTITY, [CategoryId] INT NOT NULL, [Slug] VARCHAR(120) NOT NULL, [Title] N

c# 轻量级 ORM 框架 之 DBHelper 实现 (三)

周末了比较清闲,把自己的orm框架整理了下,开源了. 已经做出来的东西通常感觉有些简单,一些新手或许听到"框架"一类的词觉得有些"高深",简单来说orm就是把ado的封装. 在介绍这个框架的第一篇博文,已经把DalBase介绍了一下设计思路,本篇的DBHelper对象也是给dalBase来用的,可以说框架的所有定义对象都是为了它. 这里起名叫DBHelper,因为我也是从写SQLHelper开始的,DBHelper只不过是所有类型对ado操作的各种方法的封装,所以本

c# 轻量级ORM框架 之 WhereHelper (二)

上篇文章发布了一些设计orm框架基层的和实现,有朋友提出WhereHelper是亮点,能被认可我表示高兴. 我就把WhereHelper设计思想和代码公开下. WhereHelper 的概念就是再拼接where 条件,为了能兼容各种数据库和参数化查询,故封装了该对象. 首先根据我的框架结构: 1.Common库 这里主要定义了,所有层都访问的类型及常用方法,因为是介绍WhereHelper的实现,对其它就不做详细解释了. WhereHelper定义到这一层是想着UI会用到该查询,故把该类型的定义

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

一.课程介绍 本次分享课程包含两个部分<.NET轻量级ORM框架Dapper修炼手册>和<.NET轻量级ORM框架Dapper葵花宝典>,阿笨将带领大家一起领略轻量级ORM框架Dapper的魅力. 本次分享课程适合人群范围: 一.<.NET轻量级ORM框架Dapper修炼手册>适合人群如下: 1.我是一个新手,主要工作面向的是MS SQL Server数据库,那么选择修炼手册一定没错. 2.对Dapper从没有过了解或者了解不全面的同学,想通过学习进一步提升对Dappe

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

1.说明 本项目是一个使用.NET Standard 2.0开发的,基于 Dapper 的轻量级 ORM 框架,包含基本的CRUD以及根据表达式进行一些操作的方法,目前只针对单表,不包含多表连接操作. github:https://github.com/iamoldli/NetSql 2.使用方法 2.2.安装 Install-Package NetSql 2.2.创建实体 创建Article实体类,继承EntityBase public class Article : EntityBase {

.NET轻量级ORM框架Dapper修炼手册

一.摘要 1.1.为什么叫本次的分享课叫<修炼手册>? 阿笨希望本次的分享课中涉及覆盖的一些小技巧.小技能给您带来一些帮助.希望您在日后工作中把它作为一本实际技能手册进行储备,以备不时之需,一旦当手头遇到与Dapper修炼手册中相似用法的地方和场景,可以直接拿来进行翻阅并灵活的运用到项目中.最后阿笨建议您可以根据自己在工作中碰到的不同的使用场景,不断的完善此本修炼手册. 废话不多说,直接上干货,我们不生产干货,我们只是干货的搬运工. 四.涉及覆盖的知识点 1.C# Linq To Xml技术.

轻量级ORM框架--PetaPoCo

ORM框架在开发中有着非常重要的作用,我之前用过也好几个了有些是公司自己开发的,还有些第三方的,但是如果在一个不是很大型的项目中,我觉得这个PetaPoCo是完全可以胜任了的使用也比较简单,接下去我简单介绍下.   首先在项目中添加petapoco: 然后配置下数据库连接参数和输入命名空间(当然必须现在配置文件中添加节点哦): 然后保存下就会自动生成数据库对应的实体(非常方便),下面是做测试的简单的增删改查: 1 //查询 2 var v = db.Fetch<BTest>("sel