• 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; }
        }
    View Code

    下面是一个实体类使用特性的例子,它指明了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;
            }
    View Code
            /// <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;
            }  
    View Code

    解析特性我们不需要知道该类的具体实例,所以这里用了泛型,只需要知道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解析类的实现 相对设计来说比较简单.

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

  • 相关阅读:
    Web基础 网页的血肉CSS
    18
    19
    20
    17
    16
    15
    13
    14
    12
  • 原文地址:https://www.cnblogs.com/xtdhb/p/3813137.html
Copyright © 2020-2023  润新知