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

來源:互聯網
上載者:User

標籤:style   blog   http   color   使用   資料   

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

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

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.