您好,登錄后才能下訂單哦!
最近一直在忙新公司的基礎(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)給出署名]
當(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)用者使用。
圖:
我們的要求就是ORM中不能存在一個(gè)反射的代碼。所以我們約定了BasicEntityObject抽象類,通過定義頂層抽象基類來包含子類所要用到的一些屬性信息。
我們看一下抽象類中包含了哪些東西。
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Collections;
- namespace Glory.Net.ORM
- {
- public abstract class BaseEntityObject : DictionaryBase
- {
- /// <summary>
- /// 實(shí)體對(duì)象對(duì)應(yīng)數(shù)據(jù)庫(kù)中的表名
- /// </summary>
- private string _tablename = string.Empty;
- /// <summary>
- /// 受保護(hù)字典:實(shí)體類中對(duì)應(yīng)數(shù)據(jù)庫(kù)表中主鍵的屬性和屬性類型
- /// </summary>
- protected Dictionary<string, string> _primarydictionary = new Dictionary<string, string>();
- /// <summary>
- /// 用于實(shí)體子類設(shè)置當(dāng)前子類對(duì)應(yīng)數(shù)據(jù)庫(kù)中的表名
- /// </summary>
- protected string TableName
- {
- get { return _tablename; }
- set { _tablename = value; }
- }
- /// <summary>
- /// 客戶代碼獲取當(dāng)前實(shí)例對(duì)應(yīng)ORM中的表名
- /// </summary>
- public string GetTableName
- {
- get { return _tablename; }
- }
- /// <summary>
- /// 用于實(shí)體子類設(shè)置當(dāng)前實(shí)例的屬性值
- /// </summary>
- /// <param name="key"></param>
- /// <returns></returns>
- protected object this[string key]
- {
- get { return this.Dictionary[key]; }
- set { this.Dictionary[key] = value; }
- }
- /// <summary>
- /// 設(shè)置實(shí)例的屬性值
- /// </summary>
- /// <param name="key">屬性名稱</param>
- /// <param name="value">屬性值</param>
- public void SetEntityVlues(string key, object value)
- {
- if (this.Dictionary.Contains(key))
- {
- this.Dictionary[key] = value;
- }
- }
- /// <summary>
- /// 獲取實(shí)例的屬性鍵值隊(duì)
- /// </summary>
- /// <returns></returns>
- public IDictionary GetEntityValue
- {
- get{return (IDictionary)this.Dictionary;}
- }
- /// <summary>
- /// 獲取實(shí)例的主鍵信息
- /// </summary>
- public IDictionary GetEntityPrimary
- {
- get { return (IDictionary)_primarydictionary; }
- }
- protected abstract void AddPrimaryToDictionary();
- }
- }
其實(shí)代碼很簡(jiǎn)單,就是為了將子類的屬性值保存到基類中來,讓子類只是一個(gè)空殼子。
- public class Tb_Car : VluesInitTb_Car
- {
- /// <summary>
- /// 唯一主鍵
- /// </summary>
- public string CID
- {
- get { return this["CID"] as string; }
- set { this["CID"] = value; }
- }
- /// <summary>
- /// 車牌號(hào)
- /// </summary>
- public string CarBanrdCode
- {
- get { return this["CarBanrdCode"] as string; }
- set { this["CarBanrdCode"] = value; }
- }
- /// <summary>
- ///
- /// </summary>
- public string CarCode
- {
- get { return this["CarCode"] as string; }
- set { this["CarCode"] = value; }
- }
- /// <summary>
- ///
- /// </summary>
- public string DriverName
- {
- get { return this["DriverName"] as string; }
- set { this["DriverName"] = value; }
- }
- /// <summary>
- /// 聯(lián)系電話
- /// </summary>
- public string Mobile
- {
- get { return this["Mobile"] as string; }
- set { this["Mobile"] = value; }
- }
- /// <summary>
- /// 車型
- /// </summary>
- public string CarType
- {
- get { return this["CarType"] as string; }
- set { this["CarType"] = value; }
- }
- /// <summary>
- /// 購(gòu)車日期
- /// </summary>
- public DateTime? BuyDateTime
- {
- get { return this["BuyDateTime"] as DateTime?; }
- set { this["BuyDateTime"] = value; }
- }
- /// <summary>
- /// 所屬中心編號(hào)。外鍵
- /// </summary>
- public string AttachCenter
- {
- get { return this["AttachCenter"] as string; }
- set { this["AttachCenter"] = value; }
- }
- /// <summary>
- /// 所屬區(qū)部編號(hào)。外鍵
- /// </summary>
- public string AttachSection
- {
- get { return this["AttachSection"] as string; }
- set { this["AttachSection"] = value; }
- }
- /// <summary>
- /// 所屬站點(diǎn)編號(hào)。外鍵
- /// </summary>
- public string AttachStop
- {
- get { return this["AttachStop"] as string; }
- set { this["AttachStop"] = value; }
- }
- }
那么中間的類是干嘛用的呢,是為了將初始化隔離在基類中;
- [Serializable()]
- public class VluesInitTb_Car : BaseEntityObject
- {
- public VluesInitTb_Car()
- {
- this.TableName = "Tb_Car";
- /// <summary>
- /// 唯一主鍵
- /// </summary>
- this.Dictionary.Add("CID", null);
- /// <summary>
- /// 車牌號(hào)
- /// </summary>
- this.Dictionary.Add("CarBanrdCode", null);
- /// <summary>
- ///
- /// </summary>
- this.Dictionary.Add("CarCode", null);
- /// <summary>
- ///
- /// </summary>
- this.Dictionary.Add("DriverName", null);
- /// <summary>
- /// 聯(lián)系電話
- /// </summary>
- this.Dictionary.Add("Mobile", null);
- /// <summary>
- /// 車型
- /// </summary>
- this.Dictionary.Add("CarType", null);
- /// <summary>
- /// 購(gòu)車日期
- /// </summary>
- this.Dictionary.Add("BuyDateTime", null);
- /// <summary>
- /// 所屬中心編號(hào)。外鍵
- /// </summary>
- this.Dictionary.Add("AttachCenter", null);
- /// <summary>
- /// 所屬區(qū)部編號(hào)。外鍵
- /// </summary>
- this.Dictionary.Add("AttachSection", null);
- /// <summary>
- /// 所屬站點(diǎn)編號(hào)。外鍵
- /// </summary>
- this.Dictionary.Add("AttachStop", null);
- AddPrimaryToDictionary();
- }
- /// <summary>
- /// 實(shí)體類 重寫實(shí)體基類添加主鍵信息方法,主鍵數(shù)據(jù)類型首字母要大寫
- /// </summary>
- protected override void AddPrimaryToDictionary()
- {
- _primarydictionary.Add("CID", "string");
- }
- }
通過這種層次的抽象可以很好的規(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)給出署名]
免責(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)容。