溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

利用抽象、多態(tài)實(shí)現(xiàn)無反射的綠色環(huán)保ORM框架

發(fā)布時(shí)間:2020-06-20 08:53:04 來源:網(wǎng)絡(luò) 閱讀:2523 作者:王清培 欄目:編程語言

最近一直在忙新公司的基礎(chǔ)庫(kù)建設(shè),對(duì)系統(tǒng)架構(gòu)、開發(fā)框架及快速開發(fā)平臺(tái)的設(shè)計(jì)實(shí)施都積累了一定的實(shí)踐經(jīng)驗(yàn)。

一般的中小型的軟件開發(fā)公司,如果按照技術(shù)儲(chǔ)備來衡量軟件項(xiàng)目的技術(shù)含量的評(píng)定依據(jù)是可行的。但如果光是按照人頭來衡量軟件的技術(shù)含量是不可靠的。所以我們?cè)谶x擇跳巢的時(shí)候是選擇大公司還是選擇有技術(shù)含量的公司要根據(jù)自己的職業(yè)規(guī)劃來。(本人最近體會(huì)到的一點(diǎn)跳巢經(jīng)驗(yàn)分享給大家)

由于我現(xiàn)有單位技術(shù)部門剛剛成立不久,需要一些基礎(chǔ)的開發(fā)框架,ORM當(dāng)然是跑不了的。在后面的文章中我將陸續(xù)寫下我在建設(shè)基礎(chǔ)框架中的一些實(shí)踐檢驗(yàn),里面可能包括對(duì)UI層的封裝、基礎(chǔ)控件的封裝等等。我就廢話少扯了,進(jìn)入主題。

這篇文章的重點(diǎn)是無反射的ORM框架,為什么會(huì)有這樣的想法?其實(shí)前不久群里的朋友就問了一些問題,他們?cè)跇?gòu)建自己的ORM框架的時(shí)候頻繁的在使用反射來編寫功能。從跟他們的交流上來看他們似乎很喜歡使用反射來寫功能,但是沒有仔細(xì)的研究過ORM框架的作用是在系統(tǒng)架構(gòu)的哪個(gè)位置,在對(duì)性能要求十分嚴(yán)格的情況下反射會(huì)有點(diǎn)無能為力。

反射的好處當(dāng)然是毋庸置疑的,一些技術(shù)稍微好點(diǎn)的或者大牛們通常會(huì)用動(dòng)態(tài)編譯的技術(shù)來平滑的過度這個(gè)系統(tǒng)最重要的性能瓶頸點(diǎn)。我總覺的可以用高層的抽象和約定來解決這個(gè)ORM使用反射的問題。下面我們來分析一下通常ORM框架為什么需要用反射,反射的目的是什么。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

ORM中反射的目的是什么?

當(dāng)然我們這里討論的是最普通的問題也是必須的問題。

ORM框架的種類形態(tài)各異,不同的公司不同的ORM實(shí)現(xiàn)。其實(shí)目的是為了能有一套屬于自己公司的開發(fā)框架,這不是技術(shù)所定而是公司高層領(lǐng)導(dǎo)所要求的。(我們沒有說話的權(quán)利,為了保住飯碗,我們只能聽從指揮)

但是大部分的ORM框架的設(shè)計(jì)思想和實(shí)現(xiàn)思路都離不開那幾點(diǎn)的“思維實(shí)現(xiàn)約束”。我為什么要說“思維實(shí)現(xiàn)約束”,這也是我們程序員的一些通病之一吧,都喜歡用復(fù)雜的技術(shù)。不管三七二十一用了心里舒服。這是好事,為了練習(xí)技術(shù)可以理解。沒有這份好奇心這份激情我們也很難走到專家的位置。

目的之一:為了表達(dá)實(shí)體與表的對(duì)應(yīng)關(guān)系

ORM是實(shí)體與表的一種映射關(guān)系,逐漸被發(fā)展為一種復(fù)雜的技術(shù)實(shí)現(xiàn)模型。

在傳統(tǒng)的分層架構(gòu)中,在實(shí)體的定義上都會(huì)使用一個(gè)特性來標(biāo)記該實(shí)體所表示的表名稱是什么。如:

[Serializable()]

[HZ.Table(TableName = "member")]

public class Member{}

特性HZ.Table中的屬性TableName來保存靜態(tài)的表名,在ORM中通過獲取對(duì)象的類型然后反射出該類型的特性元數(shù)據(jù)。然后讀取相關(guān)成員屬性值,作為拼接SQL語句的必備條件。

目的之二:為了表達(dá)屬性與字段的對(duì)應(yīng)關(guān)系及一些主、外鍵

ORM中將實(shí)體的屬性映射成數(shù)據(jù)庫(kù)中表的字段,一般通過兩種方式來表達(dá)這中關(guān)系。

第一種:通過屬性特性來表示該屬性代表的字段名稱;

[HZ.Column(PrimaryKey = true,ColumnName=”MemberId”)]

public string MemberCode { get; set; }

第二種:直接通過屬性名稱來表示字段的名稱;

public string MemberId { get; set; }

目的之三:獲取實(shí)體屬性中的值

在進(jìn)行插入或更新的時(shí)候需要獲取實(shí)體中的屬性的值,這個(gè)時(shí)候只能使用反射的方式獲取到屬性的值,然后拼接插入或更新語句。

目的之四:設(shè)置實(shí)體屬性的值

通過實(shí)例化泛型對(duì)象,然后反射對(duì)象的屬性通過SetValue方法設(shè)置屬性的值。

簡(jiǎn)結(jié):這幾點(diǎn)是最常用的,可能還包括其他復(fù)雜的功能,這里我就不涉及了。上面這幾點(diǎn)都是通過反射獲取實(shí)體的信息,不管是增、刪、改、查都需要反射。尤其是對(duì)于查詢數(shù)據(jù)來說,如果是大數(shù)據(jù)量的查詢性能問題很嚇人。

通過抽象、多態(tài)設(shè)計(jì)不需要特性的ORM實(shí)體

大部分ORM框架是需要代碼生成器做支持的,不是所有的代碼都是需要程序員手動(dòng)去敲的,可以通過一些模板引擎類的代碼生成器,編輯好自己的模板然后生成大部分的實(shí)體代碼。包括.NET里面的EntityFramework、LinqToSql也是用IDE集成的代碼生成器。

所以這里就會(huì)涉及到對(duì)企業(yè)代碼生成器的考慮,這里就先不扯了,后續(xù)文章我們?cè)賮磲槍?duì)性討論。

那么我們先來討論如何設(shè)計(jì)實(shí)體結(jié)構(gòu),讓它能包含我們ORM所需要的必備信息。其實(shí)我們的思路稍微轉(zhuǎn)變一下利用抽象來解決問題。提高抽象層次,將實(shí)體視為兩個(gè)層面。頂層抽象類被ORM使用,子類被調(diào)用者使用。

圖:

利用抽象、多態(tài)實(shí)現(xiàn)無反射的綠色環(huán)保ORM框架

我們的要求就是ORM中不能存在一個(gè)反射的代碼。所以我們約定了BasicEntityObject抽象類,通過定義頂層抽象基類來包含子類所要用到的一些屬性信息。

我們看一下抽象類中包含了哪些東西。

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Collections;  
  5.  
  6. namespace Glory.Net.ORM  
  7. {  
  8.     public abstract class BaseEntityObject : DictionaryBase  
  9.     {  
  10.         /// <summary>  
  11.         /// 實(shí)體對(duì)象對(duì)應(yīng)數(shù)據(jù)庫(kù)中的表名  
  12.         /// </summary>  
  13.         private string _tablename = string.Empty;  
  14.         /// <summary>  
  15.         /// 受保護(hù)字典:實(shí)體類中對(duì)應(yīng)數(shù)據(jù)庫(kù)表中主鍵的屬性和屬性類型  
  16.         /// </summary>  
  17.         protected Dictionary<stringstring> _primarydictionary = new Dictionary<stringstring>();  
  18.         /// <summary>  
  19.         /// 用于實(shí)體子類設(shè)置當(dāng)前子類對(duì)應(yīng)數(shù)據(jù)庫(kù)中的表名  
  20.         /// </summary>  
  21.         protected string TableName  
  22.         {  
  23.             get { return _tablename; }  
  24.             set { _tablename = value; }  
  25.         }  
  26.         /// <summary>  
  27.         /// 客戶代碼獲取當(dāng)前實(shí)例對(duì)應(yīng)ORM中的表名  
  28.         /// </summary>  
  29.         public string GetTableName  
  30.         {  
  31.             get { return _tablename; }  
  32.         }  
  33.         /// <summary>  
  34.         /// 用于實(shí)體子類設(shè)置當(dāng)前實(shí)例的屬性值  
  35.         /// </summary>  
  36.         /// <param name="key"></param>  
  37.         /// <returns></returns>  
  38.         protected object this[string key]  
  39.         {  
  40.             get { return this.Dictionary[key]; }  
  41.             set { this.Dictionary[key] = value; }  
  42.         }  
  43.         /// <summary>  
  44.         /// 設(shè)置實(shí)例的屬性值  
  45.         /// </summary>  
  46.         /// <param name="key">屬性名稱</param>  
  47.         /// <param name="value">屬性值</param>  
  48.         public void SetEntityVlues(string key, object value)  
  49.         {  
  50.             if (this.Dictionary.Contains(key))  
  51.             {  
  52.                 this.Dictionary[key] = value;  
  53.             }  
  54.         }  
  55.         /// <summary>  
  56.         /// 獲取實(shí)例的屬性鍵值隊(duì)  
  57.         /// </summary>  
  58.         /// <returns></returns>  
  59.         public IDictionary GetEntityValue  
  60.         {  
  61.             get{return (IDictionary)this.Dictionary;}  
  62.         }  
  63.         /// <summary>  
  64.         /// 獲取實(shí)例的主鍵信息  
  65.         /// </summary>  
  66.         public IDictionary GetEntityPrimary  
  67.         {  
  68.             get { return (IDictionary)_primarydictionary; }  
  69.         }  
  70.         protected abstract void AddPrimaryToDictionary();  
  71.     }  
  72. }  

其實(shí)代碼很簡(jiǎn)單,就是為了將子類的屬性值保存到基類中來,讓子類只是一個(gè)空殼子。

  1. public class Tb_Car : VluesInitTb_Car  
  2.    {  
  3.        /// <summary>  
  4.        /// 唯一主鍵  
  5.        /// </summary>  
  6.        public string CID  
  7.        {  
  8.            get { return this["CID"as string; }  
  9.            set { this["CID"] = value; }  
  10.        }  
  11.        /// <summary>  
  12.        /// 車牌號(hào)  
  13.        /// </summary>  
  14.        public string CarBanrdCode  
  15.        {  
  16.            get { return this["CarBanrdCode"as string; }  
  17.            set { this["CarBanrdCode"] = value; }  
  18.        }  
  19.        /// <summary>  
  20.        ///   
  21.        /// </summary>  
  22.        public string CarCode  
  23.        {  
  24.            get { return this["CarCode"as string; }  
  25.            set { this["CarCode"] = value; }  
  26.        }  
  27.        /// <summary>  
  28.        ///   
  29.        /// </summary>  
  30.        public string DriverName  
  31.        {  
  32.            get { return this["DriverName"as string; }  
  33.            set { this["DriverName"] = value; }  
  34.        }  
  35.        /// <summary>  
  36.        /// 聯(lián)系電話  
  37.        /// </summary>  
  38.        public string Mobile  
  39.        {  
  40.            get { return this["Mobile"as string; }  
  41.            set { this["Mobile"] = value; }  
  42.        }  
  43.        /// <summary>  
  44.        /// 車型  
  45.        /// </summary>  
  46.        public string CarType  
  47.        {  
  48.            get { return this["CarType"as string; }  
  49.            set { this["CarType"] = value; }  
  50.        }  
  51.        /// <summary>  
  52.        /// 購(gòu)車日期  
  53.        /// </summary>  
  54.        public DateTime? BuyDateTime  
  55.        {  
  56.            get { return this["BuyDateTime"as DateTime?; }  
  57.            set { this["BuyDateTime"] = value; }  
  58.        }  
  59.        /// <summary>  
  60.        /// 所屬中心編號(hào)。外鍵  
  61.        /// </summary>  
  62.        public string AttachCenter  
  63.        {  
  64.            get { return this["AttachCenter"as string; }  
  65.            set { this["AttachCenter"] = value; }  
  66.        }  
  67.        /// <summary>  
  68.        /// 所屬區(qū)部編號(hào)。外鍵  
  69.        /// </summary>  
  70.        public string AttachSection  
  71.        {  
  72.            get { return this["AttachSection"as string; }  
  73.            set { this["AttachSection"] = value; }  
  74.        }  
  75.        /// <summary>  
  76.        /// 所屬站點(diǎn)編號(hào)。外鍵  
  77.        /// </summary>  
  78.        public string AttachStop  
  79.        {  
  80.            get { return this["AttachStop"as string; }  
  81.            set { this["AttachStop"] = value; }  
  82.        }  
  83.    } 

那么中間的類是干嘛用的呢,是為了將初始化隔離在基類中;

  1. [Serializable()]  
  2.    public class VluesInitTb_Car : BaseEntityObject  
  3.    {  
  4.        public VluesInitTb_Car()  
  5.        {  
  6.            this.TableName = "Tb_Car";  
  7.            /// <summary>  
  8.            /// 唯一主鍵  
  9.            /// </summary>  
  10.            this.Dictionary.Add("CID"null);  
  11.            /// <summary>  
  12.            /// 車牌號(hào)  
  13.            /// </summary>  
  14.            this.Dictionary.Add("CarBanrdCode"null);  
  15.            /// <summary>  
  16.            ///   
  17.            /// </summary>  
  18.            this.Dictionary.Add("CarCode"null);  
  19.            /// <summary>  
  20.            ///   
  21.            /// </summary>  
  22.            this.Dictionary.Add("DriverName"null);  
  23.            /// <summary>  
  24.            /// 聯(lián)系電話  
  25.            /// </summary>  
  26.            this.Dictionary.Add("Mobile"null);  
  27.            /// <summary>  
  28.            /// 車型  
  29.            /// </summary>  
  30.            this.Dictionary.Add("CarType"null);  
  31.            /// <summary>  
  32.            /// 購(gòu)車日期  
  33.            /// </summary>  
  34.            this.Dictionary.Add("BuyDateTime"null);  
  35.            /// <summary>  
  36.            /// 所屬中心編號(hào)。外鍵  
  37.            /// </summary>  
  38.            this.Dictionary.Add("AttachCenter"null);  
  39.            /// <summary>  
  40.            /// 所屬區(qū)部編號(hào)。外鍵  
  41.            /// </summary>  
  42.            this.Dictionary.Add("AttachSection"null);  
  43.            /// <summary>  
  44.            /// 所屬站點(diǎn)編號(hào)。外鍵  
  45.            /// </summary>  
  46.            this.Dictionary.Add("AttachStop"null);  
  47.            AddPrimaryToDictionary();  
  48.        }  
  49.  
  50.        /// <summary>  
  51.        /// 實(shí)體類 重寫實(shí)體基類添加主鍵信息方法,主鍵數(shù)據(jù)類型首字母要大寫  
  52.        /// </summary>  
  53.        protected override void AddPrimaryToDictionary()  
  54.        {  
  55.            _primarydictionary.Add("CID""string");  
  56.        }  
  57.    } 

通過這種層次的抽象可以很好的規(guī)避特性帶來的性能問題。在ORM中我們的泛型方法都是約束實(shí)體為BaseEntityObject類型,然后所有的信息包括主鍵、字段、數(shù)據(jù)類型都能夠通過多態(tài)的方式獲取到。

ORM通過實(shí)例化一個(gè)對(duì)象的實(shí)例然后將其緩存起來,作為后續(xù)使用。而不需要頻繁的實(shí)例化中間對(duì)象帶來的性能問題。

其實(shí)大部分的代碼都是可以通過代碼生成器生成的,我們也正在為公司開發(fā)符合自己公司產(chǎn)品的代碼生成器,包括對(duì)業(yè)務(wù)代碼的高度抽象、業(yè)務(wù)建模后的代碼生成。

當(dāng)然該篇文章只是簡(jiǎn)單的講解了一下核心的內(nèi)容,也算是拋磚引玉吧。希望對(duì)大家來說有點(diǎn)啟發(fā)作用。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI