最近趁著不忙,在構(gòu)思一個(gè)搭建一個(gè)開源的完整項(xiàng)目,至于原因以及整個(gè)項(xiàng)目框架后邊文章我再說明。既然要起一個(gè)完整的項(xiàng)目,那么數(shù)據(jù)倉儲(chǔ)訪問就必不可少,這篇文章我主要介紹這個(gè)新項(xiàng)目(OSS.Core)中我對(duì)倉儲(chǔ)層的簡單思考和實(shí)現(xiàn)過程(當(dāng)前項(xiàng)目還處在搭建階段),主要集中在以下幾個(gè)方面:
10年積累的成都網(wǎng)站制作、做網(wǎng)站經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有利通免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
1. 數(shù)據(jù)倉儲(chǔ)層的需求
2. ORM框架選擇
3. OSS.Core倉儲(chǔ)層設(shè)計(jì)實(shí)現(xiàn)
4. 調(diào)用示例
下邊的實(shí)現(xiàn)部分中可能需要你對(duì).NET的 泛型,委托,擴(kuò)展,表達(dá)式等有一個(gè)基礎(chǔ)了解。正是因?yàn)檫@些語言特性,方便我們對(duì)操作共性的抽取統(tǒng)一。
一. 數(shù)據(jù)倉儲(chǔ)層需求
既然是一個(gè)完整的項(xiàng)目,數(shù)據(jù)訪問是其最基本的部分,同時(shí),數(shù)據(jù)訪問也是整個(gè)項(xiàng)目最容易出現(xiàn)瓶頸的地方。在我的劃分中,其承擔(dān)的角色是負(fù)責(zé)整個(gè)數(shù)據(jù)的輸入輸出,不僅僅是針對(duì)單數(shù)據(jù)庫(有時(shí)甚至多庫),有時(shí)還需要完成一級(jí)緩存的實(shí)現(xiàn),給邏輯層提供最基礎(chǔ)的數(shù)據(jù)支撐。
業(yè)務(wù)永遠(yuǎn)是在變化的,那么項(xiàng)目也要具備快速演進(jìn)的能力,所以我希望數(shù)據(jù)層能夠保持相對(duì)的簡單,在結(jié)構(gòu)上盡量減少復(fù)雜的耦合查詢,在性能上盡量減少不必要的消耗,例如反射的大量使用。同時(shí)針對(duì)每個(gè)業(yè)務(wù)對(duì)象完成數(shù)據(jù)庫層面基本的CRUD統(tǒng)一封裝實(shí)現(xiàn)。如果有需要的時(shí)候還能在最少的改動(dòng)下加入緩存的更新。(對(duì)于如何實(shí)現(xiàn)不同模塊不同緩存存儲(chǔ)策略,像redis,Memcached會(huì)在后邊文章介紹)
同時(shí),對(duì)于一個(gè)稍微有點(diǎn)規(guī)模的項(xiàng)目來說,解決數(shù)據(jù)庫訪問的最快速做法就是實(shí)現(xiàn)讀寫分離,所以,我希望這個(gè)框架能夠在一開始在底層就實(shí)現(xiàn)了讀寫分離的支持,以避免后期再重頭對(duì)業(yè)務(wù)代碼的大量修改。
二. ORM 框架選擇
當(dāng)然,如果為了簡單和性能,直接ADO.NET連接理論上來說是比較高效的做法,不過這樣會(huì)造成大量的重復(fù)操作邏輯代碼,同時(shí)也會(huì)造成代碼的散亂,增加維護(hù)復(fù)雜度。作為技術(shù)人員,不僅需要解決業(yè)務(wù)問題提高效率,同時(shí)也要提高自己的效率,所以我會(huì)選擇一個(gè)ORM框架來完成部分基礎(chǔ)工作。
當(dāng)前在.NET體系下,開源的ORM框架很多,如:Entityframework,NHibernate,iBATIS.NET,Dapper等等,各有特色,基于前面我說的,保證效率的同時(shí),兼顧簡單還能最大程度減少性能的損耗,并且提供.net standard標(biāo)準(zhǔn)庫下的支持。這里對(duì)比之后我選擇Dapper這個(gè)半自動(dòng)化的ORM作為倉儲(chǔ)層的基礎(chǔ)框架,選擇原因如下:
1. 其結(jié)構(gòu)簡單,整個(gè)封裝主要集中Dapper.cs文件中,體積很小
2. 封裝功能簡單強(qiáng)大,對(duì)原生SQL的支持上很靈活
這點(diǎn)幾乎完勝其他框架,無需任何多余的設(shè)置,同時(shí)基本上你可調(diào)用所有原生ADO.NET的功能,sql語句完全自己掌控,卻又無需關(guān)心command的參數(shù)賦值,以及結(jié)果實(shí)體轉(zhuǎn)換等。
3. 性能上的高效
很多ORM的實(shí)體映射通過反射來完成,這點(diǎn)上Dapper再次展現(xiàn)其魅力,在Commond參數(shù)賦值,以及實(shí)體轉(zhuǎn)換等關(guān)鍵模塊,使用了Reflection.Emit功能,間接實(shí)現(xiàn)了MSIL編譯層面的賦值實(shí)現(xiàn),之所以說間接,是因?yàn)槠浔旧泶a還需要編譯器生成IL代碼。在運(yùn)行時(shí)根據(jù)類型屬性動(dòng)態(tài)創(chuàng)建賦值委托方法。
三. OSS.Core倉儲(chǔ)層設(shè)計(jì)實(shí)現(xiàn)
通過Dapper可以實(shí)現(xiàn)在數(shù)據(jù)庫訪問部分一層簡單的封裝,不過我依然需要手動(dòng)編寫不少的sql語句,同時(shí)還要進(jìn)行參數(shù)化的處理,包括數(shù)據(jù)的讀寫分離等。那么這些功能的實(shí)現(xiàn)我將在OSS.Core.RepDapper中完成,為了方便理解,先貼出一個(gè)簡單的封裝后的方法調(diào)用傳輸流程:

在這個(gè)圖里展示一個(gè)簡單的方法調(diào)用流程,圍繞這張圖的幾個(gè)核心部分,我分別介紹下:
1. 接口設(shè)計(jì)
因?yàn)槲蚁M@個(gè)是完整的示例項(xiàng)目,所以后邊希望能夠兼容不同數(shù)據(jù)庫,因此對(duì)外的倉儲(chǔ)訪問都基于接口調(diào)用。當(dāng)然如果你的項(xiàng)目根本沒有切換數(shù)據(jù)庫的需求,我更建議去掉這一環(huán)節(jié),直接在基類中實(shí)現(xiàn)單例模式,業(yè)務(wù)邏輯層直接調(diào)用。
圖中可以看到接口層獨(dú)立于實(shí)現(xiàn)部分,我將具體業(yè)務(wù)實(shí)體模型和接口 單獨(dú)放在了OSS.Core.DomainMos 類庫中,一方面是為了實(shí)體模型在各模塊中的共用,另一方面解耦業(yè)務(wù)邏輯層(Services)和倉儲(chǔ)層(Reps)之間的依賴關(guān)系。
同時(shí)一個(gè)項(xiàng)目中數(shù)據(jù)庫訪問代碼多數(shù)都會(huì)以CRUD為主,所以這里我定義了一個(gè)基礎(chǔ)接口(IBaseRep),其包含的方法主要有(表達(dá)式部分在后邊介紹):

具體的業(yè)務(wù)數(shù)據(jù)接口繼承至基礎(chǔ)接口就好,其中表達(dá)式部分是我自己做了一個(gè)封裝,后邊會(huì)簡單介紹。
2. 倉儲(chǔ)基類實(shí)現(xiàn)(BaseRep)
首先,如圖所示,我們實(shí)現(xiàn)了讀寫分離的兩個(gè)擴(kuò)展,其實(shí)最終都會(huì)經(jīng)過Excute方法,那么這里展示下方法的具體實(shí)現(xiàn):

可以看到在這個(gè)方法提供了一個(gè)針對(duì)IDbConnection的委托,提供調(diào)用層自由使用Dapper方法的同時(shí),統(tǒng)一了數(shù)據(jù)訪問方法入口,便于日志記錄,和排查。
其次,在很多項(xiàng)目中會(huì)出現(xiàn)用戶和訂單在不同庫中的這類情況,因?yàn)樯婕暗椒謳斓那闆r,所以需要子類中能有修改連接串能力,那么這里我通過構(gòu)造函數(shù)的形式,提供了兩個(gè)可空參數(shù):

可以看到,如果子類中定義了自己的連接串,則以子類自定義為主,否則走默認(rèn)的連接信息。
最后,我們也實(shí)現(xiàn)了針對(duì)基礎(chǔ)接口方法的具體實(shí)現(xiàn),舉一示例:

同時(shí),為了保證子類中能夠加入緩存處理,所以采用了虛方法(virtual)的形式,保證子類能夠重寫。
3. 基于Connection的擴(kuò)展
這個(gè)地方主要分為兩個(gè)部分,a. 表達(dá)式的解析,以及參數(shù)化的處理 b. 擴(kuò)展Connection的Insert,Update...等Dapper沒有擴(kuò)展的方法:
a. 熟悉Expression表達(dá)式的朋友應(yīng)該比較了解,表達(dá)式本身是一個(gè)樹形接口,根據(jù)不同的類型,可以不斷的解析其子表達(dá)式,直到不具備繼續(xù)解析的可能。所以這個(gè)就很簡單就是遞歸的不斷迭代,根據(jù)其不同的NodeType可以組裝不同的sql元素,因?yàn)榇a較長,可以參見github下的SqlExpressionVisitor.cs類,其中參數(shù)的賦值部分,沒有采用反射,而是使用的反射發(fā)射,代碼詳見SqlParameterEmit.cs
b. 有了表達(dá)式的擴(kuò)展之后,就可以獲取對(duì)應(yīng)的sql和參數(shù),通過this擴(kuò)展Connection方法即可,代碼見ConnoctionExtention.cs
四. 調(diào)用示例
1. 我們定義一個(gè)簡單UserInfoMo實(shí)體(包含mobile等屬性)
2. 定義接口 IUserInfoRep: IBaseRep
3. 定義實(shí)現(xiàn)類 UserInfoRep : BaseRep, IUserInfoRep
在不添加其他代碼的基礎(chǔ)上,我們就可以完成下面的調(diào)用:

文章標(biāo)題:OSS.Core基于Dapper封裝(表達(dá)式解析+Emit)倉儲(chǔ)層的構(gòu)思及實(shí)現(xiàn)
當(dāng)前URL:http://www.chinadenli.net/article16/pisggg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、網(wǎng)站制作、云服務(wù)器、品牌網(wǎng)站設(shè)計(jì)、品牌網(wǎng)站建設(shè)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)