ORM数据层框架的设计热点:更新指定的列的几种设计方案
ORM框架的定義:對(duì)象-關(guān)系映射(Object/Relation Mapping,簡(jiǎn)稱ORM)
常見的是:數(shù)據(jù)庫結(jié)構(gòu)=》映射Object(實(shí)體屬性)=》基于實(shí)體類的操作。
還有一種:數(shù)據(jù)庫結(jié)構(gòu)=》映射Object(內(nèi)存表結(jié)構(gòu))=》基于內(nèi)存表的操作。
當(dāng)然,如果你有創(chuàng)意,你還能創(chuàng)造出更多的映射載體來實(shí)現(xiàn)ORM。
避免思維定式:?
由于思維定式,很多開發(fā)者,只有見到基于實(shí)體類映射,才會(huì)認(rèn)為是一種ORM框架,于是很少人去思考其它映射載體來實(shí)現(xiàn)ORM。
這個(gè)思維定式,和早期在ASP.NET MVC沒出來之前,把WebForm框架當(dāng)成ASP.NET一樣,于是很少人會(huì)去創(chuàng)造另一種開發(fā)框架。
不過要避免思維定式,有時(shí)候的確不是件容易的事~~~這需要太多知識(shí)的沉淀和積累,這個(gè)就先不扯了,下面來正題。
?
ORM生成SQL:
一般的數(shù)據(jù)庫下,都是基于SQL語句解析執(zhí)行的,所以O(shè)RM最終都避不開生成SQL再交還ADO.NET去執(zhí)行,從而返回結(jié)果。
由于ORM存在映射關(guān)系,最簡(jiǎn)單的是(字段名稱+數(shù)據(jù)類型)的實(shí)體映射,因此,通常只要遍歷實(shí)體的屬性就可以拿到所有字段名稱,從而組合SQL去執(zhí)行了。
而反射的應(yīng)用,對(duì)于實(shí)體型映射的ORM無疑是佳方案,節(jié)省大量代碼;通過反射,在處理時(shí)動(dòng)態(tài)獲得指定對(duì)象的類型、字段名稱、字段類型、或者特性描述等信息,從而構(gòu)造出SQL語句。
如果是基于內(nèi)存表(MDataTable)映射的,則無需反射,因?yàn)橛成涞臅r(shí)候,相關(guān)結(jié)構(gòu)已提前預(yù)約好了,直接遍歷獲取即可。
ORM的兩種實(shí)體型實(shí)現(xiàn)方式:充血和貧血
這里先不說基于內(nèi)存表的映射,說說基于實(shí)體映射的設(shè)計(jì)方案:
充血模式的示例:
public?class?Users?:?CYQ.Data.Orm.OrmBase????{
????????public?Users()
????????{
????????????base.SetInit(this);
????????}
????????public?int?ID?{?get;?set;?}
????????public?string?UserName?{?get;?set;?}
????????public?DateTime?CreateTime?{?get;?set;?}
????}
這種方式,增刪改查,都由基類處理了,而基類一般需要三個(gè)參數(shù):
1:子類的對(duì)象,用于反射類型及屬性的需要。2:表名(可選,如果不寫,則從反射中拿到類名當(dāng)表名)
3:數(shù)據(jù)庫鏈接(可選,如果不寫,則會(huì)默認(rèn)約定一個(gè)配置名稱的數(shù)據(jù)庫鏈接)
最終的操作方式類似:
using?(Users?u?=?new?Users())????????????{
????????????????u.UserName?=?"u1";
????????????????u.Update(1);
? ? ? ? ? ? }
如果按常規(guī),我們可能會(huì)循環(huán)所有字段,全部更新;很明顯在這里我們只需要更新UserName。
于是,在設(shè)計(jì)上,我們需要額外多出一個(gè)集合,來存儲(chǔ)字段對(duì)應(yīng)的狀態(tài),這個(gè)集合怎么設(shè)計(jì),這個(gè)大伙自行發(fā)揮了。
這里的難點(diǎn),在于,如何設(shè)計(jì)獲取狀態(tài)改變上。
充血模式的兩種更新指定列的設(shè)計(jì)方案:?
1: 把動(dòng)作交給實(shí)體,這種方案比較常規(guī),動(dòng)動(dòng)腦就想的出來:
直接切入實(shí)體的Set屬性,如:
public?string?UserName????????{
????????????get;
????????????set
????????????{
????????????????base.SetState(value);
????????????}
????????}
然后事情就交給基類的SetState方法去修正對(duì)應(yīng)實(shí)體的狀態(tài),遍歷屬性的時(shí)候,再比較狀態(tài),取得只需要更新的字段去組合即可。
?
2:把動(dòng)作交給基類:利用AOP動(dòng)態(tài)攔截屬性,這種方案實(shí)現(xiàn)相對(duì)復(fù)雜
這種方案,優(yōu)點(diǎn)是:可以保持實(shí)體類的相對(duì)簡(jiǎn)潔,通過在基類利用AOP攔截子類的Set方法,從而動(dòng)態(tài)的調(diào)用SetState方法。
缺點(diǎn)是:實(shí)現(xiàn)有點(diǎn)難度,另外是由于AOP基類ContextBoundObject的限制, 內(nèi)部無法使用泛型。
即你不能實(shí)現(xiàn):Select<T>()類似的方法,所以最終的表達(dá)式可能需要借第三個(gè)類的ToList()方法來返回,代碼類似:
using(Users?u?=?new?Users()){?
?????List<Users>?list= u.Select().ToList();
}?
具體的方案實(shí)現(xiàn)可以見我以前寫的這兩篇文章:
?
C# Aop簡(jiǎn)單掃盲及ORM實(shí)體類屬性攔截示例
?
Aop RealProxy 千年遇BUG?
?
貧血模式的示例:
public?class?Users?????{
????????public?int?ID?{?get;?set;?}
????????public?string?UserName?{?get;?set;?}
????????public?DateTime?CreateTime?{?get;?set;?}
? ? }
這種方式,實(shí)體就類似數(shù)據(jù)載體,本身不具備增刪改查功能,類似把基類獨(dú)立開來操作。
最終的調(diào)用方式可能類似:
Users?user=?DBFast.Find<Users>(1);//查詢記錄。Users?u=new?Users();
u.UserName="u1";
DBFast.Update<Users>(u,1);//更新。?
貧血模式的兩種更新指定列的設(shè)計(jì)方案:
對(duì)于貧血模式,也有兩種對(duì)應(yīng)的設(shè)計(jì)方案:
1:實(shí)體還是原生態(tài)的:
public?class?Users?????{
????????public?int?ID?{?get;?set;?}
????????public?string?UserName?{?get;?set;?}
????????public?DateTime?CreateTime?{?get;?set;?}
????}
由于沒有基類,所以狀態(tài)的變化,無法很好的集成的,因此,這種情況的設(shè)計(jì),通常需要多一行額外的代碼來傳遞信息。
例如:
Users?u=new?Users();u.UserName="u1";
DBFast.SetState<Users>("UserName",State.ForUpdate);//類似的多了一行來指定需要更新的一個(gè)或多個(gè)列的狀態(tài)。
DBFast.Update<Users>(u,1);//更新。
2:可以Nullable的實(shí)體:
public?class?Users?????{
????????public?int??ID?{?get;?set;?}
????????public?string?UserName?{?get;?set;?}
????????public?DateTime??CreateTime?{?get;?set;?}
????}
對(duì)于這種模式,可以讓值類型的默認(rèn)值也為Null,因此可以通過減免值為Null的列,來實(shí)現(xiàn)更新值不為Null的值。
不過對(duì)于這種方式,由于DBNull.Value只能給引用類型賦值,因此值類型的字段無法重置為Null。
所以,如果要實(shí)現(xiàn)對(duì)值類型賦Null值,可能需要增加一行代碼來對(duì)指定的行指定狀態(tài),配合著使用;或者直接操作SQL語句。?
總結(jié)回憶:?
記得前些日子和騰訊的架構(gòu)師面試的時(shí)候,好像也交流到了這個(gè)指定列的更新的話題上,不過那時(shí)候的話題,上升到分布式話題了:
問題:大體是有一份數(shù)據(jù)載體情況下,用戶更新了某些字段,如何只更新某些字段的話題:
1:先是說前端的過濾與傳遞只需要更新的數(shù)據(jù)(對(duì)方:這個(gè)先假設(shè)沒有做)。
2:服務(wù)端可以做緩存,然后比較(對(duì)方:假設(shè)服務(wù)器有很多,做了負(fù)載,如何保證)。
3:全局共享緩存或用分布式緩存(對(duì)方:假設(shè)沒有分布式緩存,只有Web服務(wù)器緩存,只有一份副本)
4:通過某種算法,讓用戶的請(qǐng)求的數(shù)據(jù)對(duì)應(yīng)到相對(duì)的副本的服務(wù)器。(對(duì)方:算法怎么實(shí)現(xiàn))
5:。。。此處省略600字了。。。
?
不知不覺又寫了好幾個(gè)小時(shí)了,今天就介紹到這里了~~~謝謝大伙~~~~
最后給貼一下今天各大群熱傳的勵(lì)志文:月領(lǐng)1萬2小IT職員五年在北京買500萬房子~~
?
轉(zhuǎn)載于:https://www.cnblogs.com/cyq1162/p/3336662.html
總結(jié)
以上是生活随笔為你收集整理的ORM数据层框架的设计热点:更新指定的列的几种设计方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shell 学习笔记(四)
- 下一篇: Number对象