程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c# 輕量級 ORM 框架 之 Model解析 (四)

c# 輕量級 ORM 框架 之 Model解析 (四)

編輯:C#入門知識

  關於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解析類的實現 相對設計來說比較簡單.

如果有大神有啥好的建議,或有什麼不足,希望能 探討,指正 .

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved