




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
petshop40功能的使用說(shuō)明.txtゅ你不用一上線看見(jiàn)莪在線,就急著隱身,放心。莪不會(huì)去纏你。說(shuō)好的不離不棄 現(xiàn)在反而自己卻做不到╮ pettshop4.0詳詳解之一(系系統(tǒng)架構(gòu)設(shè)計(jì))
前言言:PetShop是一個(gè)范例,微軟用它來(lái)來(lái)展示.Net企企業(yè)系統(tǒng)開(kāi)發(fā)發(fā)的能力。業(yè)界界有許多.Neet與
J2EE之爭(zhēng),許多多數(shù)據(jù)是從微微軟的PetShoop和Sun的的PetStore而而來(lái)。這種爭(zhēng)爭(zhēng)論不可避免免帶有
濃厚厚的商業(yè)色彩,,對(duì)于我們開(kāi)開(kāi)發(fā)人員而言言,沒(méi)有必要過(guò)過(guò)多關(guān)注。然然而PetShopp隨著版本的的不斷
更新新,至現(xiàn)在基于于.Net2.0的的PetShop44.0為止,整整個(gè)設(shè)計(jì)逐漸變變得成熟而優(yōu)優(yōu)雅,卻又很很多可
以借借鑒之處。PeetShop是一個(gè)個(gè)小型的項(xiàng)目目,系統(tǒng)架構(gòu)構(gòu)與代碼都比較較簡(jiǎn)單,卻也也凸現(xiàn)了許多頗有
價(jià)值值的設(shè)計(jì)與開(kāi)發(fā)發(fā)理念。本系系列試圖對(duì)PetShop作一一個(gè)全方位的解剖,依據(jù)的的代碼是
PetSShop4.0,可可以從鏈接
httpp://msdn.mmicrosoft.coom/library/ddefault.aspp?url=/library/en-us/ddnbda/html//bd
asamppet4.asp中獲得。
一、PetShop的的系統(tǒng)架構(gòu)設(shè)設(shè)計(jì)
在軟軟件體系架構(gòu)設(shè)設(shè)計(jì)中,分層層式結(jié)構(gòu)是最常見(jiàn),也是最最重要的一種結(jié)構(gòu)。微軟推推薦的分層式式結(jié)構(gòu)
一般般分為三層,從從下至上分別別為:數(shù)據(jù)訪問(wèn)層、業(yè)務(wù)邏邏輯層(又或或成為領(lǐng)域?qū)樱?、表示層,如圖
所示示:
圖一:三層的分層式結(jié)構(gòu)
數(shù)據(jù)據(jù)訪問(wèn)層:有時(shí)時(shí)候也稱為是是持久層,其功功能主要是負(fù)負(fù)責(zé)數(shù)據(jù)庫(kù)的訪問(wèn)。簡(jiǎn)單的的說(shuō)法就是實(shí)實(shí)現(xiàn)對(duì)
數(shù)據(jù)據(jù)表的Selectt,Insert,UUpdate,Deelete的操作作。如果要加入入ORM的元元素,那么就會(huì)會(huì)包
括對(duì)對(duì)象和數(shù)據(jù)表之之間的mapping,以及對(duì)對(duì)象實(shí)體的持持久化。在PeetShop的數(shù)數(shù)據(jù)訪問(wèn)層中,,并
沒(méi)有有使用ORM,從而導(dǎo)致了代碼量的增加加,可以看作作是整個(gè)設(shè)計(jì)實(shí)現(xiàn)中的一大大敗筆。
業(yè)務(wù)邏輯層:是整個(gè)系統(tǒng)的核心,它與這個(gè)系統(tǒng)的業(yè)務(wù)(領(lǐng)域)有關(guān)。以PetShop為例,業(yè)務(wù)
邏輯層的相關(guān)設(shè)計(jì),均和網(wǎng)上寵物店特有的邏輯相關(guān),例如查詢寵物,下訂單,添加寵物到購(gòu)物
車(chē)等等。如果涉及到數(shù)據(jù)庫(kù)的訪問(wèn),則調(diào)用數(shù)據(jù)訪問(wèn)層。
表示層:是系統(tǒng)的UI部分,負(fù)責(zé)使用者與整個(gè)系統(tǒng)的交互。在這一層中,理想的狀態(tài)是不應(yīng)包
括系統(tǒng)的業(yè)務(wù)邏輯。表示層中的邏輯代碼,僅與界面元素有關(guān)。在PetShop中,是利用ASP.Net
來(lái)設(shè)計(jì)的,因此包含了許多Web控件和相關(guān)邏輯。
分層式結(jié)構(gòu)究竟其優(yōu)勢(shì)何在?MartinFowler在《PatternsofEnterpriseApplication
Architecture》一書(shū)中給出了答案:
1、開(kāi)發(fā)人員可以只關(guān)注整個(gè)結(jié)構(gòu)中的其中某一層;
2、可以很容易的用新的實(shí)現(xiàn)來(lái)替換原有層次的實(shí)現(xiàn);
3、可以降低層與層之間的依賴;
4、有利于標(biāo)準(zhǔn)化;
5、利于各層邏輯的復(fù)用。
概括來(lái)說(shuō),分層式設(shè)計(jì)可以達(dá)至如下目的:分散關(guān)注、松散耦合、邏輯復(fù)用、標(biāo)準(zhǔn)定義。
一個(gè)好的分層式結(jié)構(gòu),可以使得開(kāi)發(fā)人員的分工更加明確。一旦定義好各層次之間的接口,負(fù)責(zé)
不同邏輯設(shè)計(jì)的開(kāi)發(fā)人員就可以分散關(guān)注,齊頭并進(jìn)。例如UI人員只需考慮用戶界面的體驗(yàn)與
操作,領(lǐng)域的設(shè)計(jì)人員可以僅關(guān)注業(yè)務(wù)邏輯的設(shè)計(jì),而數(shù)據(jù)庫(kù)設(shè)計(jì)人員也不必為繁瑣的用戶交互
而頭疼了。每個(gè)開(kāi)發(fā)人員的任務(wù)得到了確認(rèn),開(kāi)發(fā)進(jìn)度就可以迅速的提高。
松散耦合的好處是顯而易見(jiàn)的。如果一個(gè)系統(tǒng)沒(méi)有分層,那么各自的邏輯都緊緊糾纏在一起,彼
此間相互依賴,誰(shuí)都是不可替換的。一旦發(fā)生改變,則牽一發(fā)而動(dòng)全身,對(duì)項(xiàng)目的影響極為嚴(yán)重。
降低層與層間的依賴性,既可以良好地保證未來(lái)的可擴(kuò)展,在復(fù)用性上也是優(yōu)勢(shì)明顯。每個(gè)功能
模塊一旦定義好統(tǒng)一的接口,就可以被各個(gè)模塊所調(diào)用,而不用為相同的功能進(jìn)行重復(fù)地開(kāi)發(fā)。
進(jìn)行好的分層式結(jié)構(gòu)設(shè)計(jì),標(biāo)準(zhǔn)也是必不可少的。只有在一定程度的標(biāo)準(zhǔn)化基礎(chǔ)上,這個(gè)系統(tǒng)才
是可擴(kuò)展的,可替換的。而層與層之間的通信也必然保證了接口的標(biāo)準(zhǔn)化。
“金無(wú)足赤,人無(wú)完人”,分層式結(jié)構(gòu)也不可避免具有一些缺陷:
1、降低了系統(tǒng)的性能。這是不言而喻的。如果不采用分層式結(jié)構(gòu),很多業(yè)務(wù)可以直接造訪數(shù)據(jù)
庫(kù),以此獲取相應(yīng)的數(shù)據(jù),如今卻必須通過(guò)中間層來(lái)完成。
2、有時(shí)會(huì)導(dǎo)致級(jí)聯(lián)的修改。這種修改尤其體現(xiàn)在自上而下的方向。如果在表示層中需要增加一
個(gè)功能,為保證其設(shè)計(jì)符合分層式結(jié)構(gòu),可能需要在相應(yīng)的業(yè)務(wù)邏輯層和數(shù)據(jù)訪問(wèn)層中都增加相
應(yīng)的代碼。
前面提到,PetShop的表示層是用ASP.Net設(shè)計(jì)的,也就是說(shuō),它應(yīng)是一個(gè)BS系統(tǒng)。在.Net
中,標(biāo)準(zhǔn)的BS分層式結(jié)構(gòu)如下圖所示:
圖二:.Net中標(biāo)準(zhǔn)的BSS分層式結(jié)構(gòu)構(gòu)
隨著著PetShop版版本的更新,其分層式結(jié)構(gòu)構(gòu)也在不斷的的完善,例如PetShop2.0,就沒(méi)有采采用標(biāo)
準(zhǔn)的的三層式結(jié)構(gòu),,如圖三:
圖三:PettShop2.0的的體系架構(gòu)
從圖圖中我們可以看看到,并沒(méi)有有明顯的數(shù)據(jù)據(jù)訪問(wèn)層設(shè)計(jì)。。這樣的設(shè)計(jì)計(jì)雖然提高了數(shù)據(jù)訪問(wèn)的性性能,
但也也同時(shí)導(dǎo)致了業(yè)業(yè)務(wù)邏輯層與與數(shù)據(jù)訪問(wèn)的的職責(zé)混亂。一一旦要求支持持的數(shù)據(jù)庫(kù)發(fā)生生變化,或者者需要
修改改數(shù)據(jù)訪問(wèn)的邏邏輯,由于沒(méi)沒(méi)有清晰的分層,會(huì)導(dǎo)致項(xiàng)項(xiàng)目作大的修修改。而隨著硬硬件系統(tǒng)性能能的提
高,以及充分利用用緩存、異步步處理等機(jī)制制,分層式結(jié)構(gòu)構(gòu)所帶來(lái)的性性能影響幾乎可以忽略不計(jì)計(jì)。
PetSShop3.0糾正正了此前層次次不明的問(wèn)題題,將數(shù)據(jù)訪問(wèn)問(wèn)邏輯作為單單獨(dú)的一層獨(dú)立出來(lái):
圖四:PettShop3.0的的體系架構(gòu)
PetSShop4.0基本本上延續(xù)了33.0的結(jié)構(gòu),但在性能上作作了一定的改改進(jìn),引入了了緩存和異步處處理
機(jī)制制,同時(shí)又充分分利用了ASSP.Net2.0的的新功能MemberShip,因此PetShhop4.0的系統(tǒng)架
構(gòu)圖圖如下所示:
圖五:PettShop4.0的的體系架構(gòu)
比較較3.0和4.0的系統(tǒng)架構(gòu)圖圖,其核心的的內(nèi)容并沒(méi)有發(fā)生變化。在在數(shù)據(jù)訪問(wèn)層層(DAL)中,仍
然采采用DALInteerface抽象出出數(shù)據(jù)訪問(wèn)邏邏輯,并以DAALFactory作作為數(shù)據(jù)訪問(wèn)問(wèn)層對(duì)象的工工廠模
塊。對(duì)于DALInterface而言言,分別有支支持MS-SQLL的SQLSeerverDAL和和支持Oracle的
OracleDAL具體體實(shí)現(xiàn)。而Model模塊則包含了數(shù)據(jù)實(shí)實(shí)體對(duì)象。其詳詳細(xì)的模塊結(jié)結(jié)構(gòu)圖如下所所示:
圖六:數(shù)據(jù)據(jù)訪問(wèn)層的模模塊結(jié)構(gòu)圖
可以以看到,在數(shù)據(jù)據(jù)訪問(wèn)層中,完全采用了““面向接口編程”思想。抽象出來(lái)的IDDAL模塊,脫脫離了
與具具體數(shù)據(jù)庫(kù)的依依賴,從而使使得整個(gè)數(shù)據(jù)據(jù)訪問(wèn)層利于數(shù)數(shù)據(jù)庫(kù)遷移。DALFactorry模塊專(zhuān)門(mén)管管理
DALL對(duì)象的創(chuàng)建建,便于業(yè)務(wù)邏邏輯層訪問(wèn)。SQLServerDAL和OraacleDAL模塊塊均實(shí)現(xiàn)IDAAL模
塊的的接口,其中包包含的邏輯就就是對(duì)數(shù)據(jù)庫(kù)庫(kù)的Select,IInsert,Upddate和Deleete操作。因因?yàn)閿?shù)
據(jù)庫(kù)庫(kù)類(lèi)型的不同,,對(duì)數(shù)據(jù)庫(kù)的的操作也有所所不同,代碼也也會(huì)因此有所所區(qū)別。
此外外,抽象出來(lái)的的IDAL模塊塊,除了解除了了向下的依賴賴之外,對(duì)于其上的業(yè)務(wù)邏邏輯層,同樣樣僅存
在弱弱依賴關(guān)系,如如下圖所示:
圖七:業(yè)務(wù)務(wù)邏輯層的模模塊結(jié)構(gòu)圖
圖七七中BLL是業(yè)業(yè)務(wù)邏輯層的核核心模塊,它它包含了整個(gè)個(gè)系統(tǒng)的核心業(yè)業(yè)務(wù)。在業(yè)務(wù)務(wù)邏輯層中,不不能
直接接訪問(wèn)數(shù)據(jù)庫(kù),,而必須通過(guò)過(guò)數(shù)據(jù)訪問(wèn)層層。注意圖中對(duì)對(duì)數(shù)據(jù)訪問(wèn)業(yè)業(yè)務(wù)的調(diào)用,是通過(guò)接口模模塊
IDAAL來(lái)完成的。既然與具體體的數(shù)據(jù)訪問(wèn)邏邏輯無(wú)關(guān),則則層與層之間的關(guān)系就是松松散耦合的。如果
此時(shí)時(shí)需要修改數(shù)據(jù)據(jù)訪問(wèn)層的具具體實(shí)現(xiàn),只要要不涉及到IIDAL的接口定義,那么業(yè)業(yè)務(wù)邏輯層就就不會(huì)
受到到任何影響。畢畢竟,具體實(shí)實(shí)現(xiàn)的SQLServerDAL和和OracalDAAL根本就與業(yè)業(yè)務(wù)邏輯層沒(méi)沒(méi)有半
點(diǎn)關(guān)關(guān)系。
因?yàn)闉樵赑etShopp4.0中引入了異步處理機(jī)機(jī)制。插入訂訂單的策略可以分為同步和和異步,兩者者的插
入策策略明顯不同,,但對(duì)于調(diào)用用者而言,插插入訂單的接口口是完全一樣樣的,所以PeetShop4.0中設(shè)
計(jì)了了IBLLStrateegy模塊。雖雖然在IBLLSStrategy模塊塊中,僅僅是是簡(jiǎn)單的IOrdyy,但
derStateg
同時(shí)時(shí)也給出了一個(gè)個(gè)范例和信息息,那就是在業(yè)務(wù)邏輯的處處理中,如果果存在業(yè)務(wù)操作作的多樣化,或者
是今今后可能的變化化,均應(yīng)利用用抽象的原理。或者使用接接口,或者使用抽象類(lèi),從從而脫離對(duì)具具體業(yè)
務(wù)的的依賴。不過(guò)在在PetShop中,由于業(yè)務(wù)務(wù)邏輯相對(duì)簡(jiǎn)簡(jiǎn)單,這種思想想體現(xiàn)得不夠夠明顯。也正正因?yàn)?/p>
此,PetShop將將核心的業(yè)務(wù)邏邏輯都放到了了一個(gè)模塊BLL中,并沒(méi)有有將具體的實(shí)實(shí)現(xiàn)和抽象嚴(yán)嚴(yán)格的
按照照模塊分開(kāi)。所所以表示層和和業(yè)務(wù)邏輯層層之間的調(diào)用關(guān)關(guān)系,其耦合合度相對(duì)較高:
圖八:表表示層的模塊塊結(jié)構(gòu)圖
在圖圖五中,各個(gè)層層次中還引入入了輔助的模模塊,如數(shù)據(jù)訪訪問(wèn)層的Messsaging模塊塊,是為異步步插入
訂單單的功能提供,,采用了MSSMQ(MicroosoftMessaagingQueuee)技術(shù)。而而表示層的
CaccheDependeency則提供緩存功能。這這些特殊的模模塊,我會(huì)在此此后的文章中中詳細(xì)介紹。
pettshop4.0詳詳解之二(數(shù)數(shù)據(jù)訪問(wèn)層之?dāng)?shù)據(jù)庫(kù)訪問(wèn)設(shè)設(shè)計(jì))
在系系列一中,我從從整體上分析析了PetShop的架構(gòu)設(shè)計(jì)計(jì),并提及了了分層的概念。從本部分開(kāi)開(kāi)始,
我將將依次對(duì)各層進(jìn)進(jìn)行代碼級(jí)的的分析,以求求獲得更加細(xì)致致而深入的理理解。在PetSShop4.0中中,由
于引引入了ASP.NNet2.0的一些新特色,所所以數(shù)據(jù)層的的內(nèi)容也更加的廣泛和復(fù)雜雜,包括:數(shù)數(shù)據(jù)庫(kù)
訪問(wèn)問(wèn)、Messaginng、MembeerShip、Proofile四部分。在系列二中,我將介紹有有關(guān)數(shù)據(jù)庫(kù)訪訪問(wèn)的
設(shè)計(jì)計(jì)。
在PPetShop中,系統(tǒng)需要處處理的數(shù)據(jù)庫(kù)對(duì)對(duì)象分為兩類(lèi)類(lèi):一是數(shù)據(jù)實(shí)體,對(duì)應(yīng)數(shù)數(shù)據(jù)庫(kù)中相應(yīng)應(yīng)的數(shù)
據(jù)表表。它們沒(méi)有行行為,僅用于于表現(xiàn)對(duì)象的的數(shù)據(jù)。這些實(shí)實(shí)體類(lèi)都被放放到Model程程序集中,例如數(shù)
據(jù)表表Order對(duì)應(yīng)應(yīng)的實(shí)體類(lèi)OrrderInfo,其其類(lèi)圖如下:
這些些對(duì)象并不具有有持久化的功功能,簡(jiǎn)單地說(shuō),它們是作作為數(shù)據(jù)的載載體,便于業(yè)務(wù)務(wù)邏輯針對(duì)相相應(yīng)數(shù)
據(jù)表表進(jìn)行讀/寫(xiě)操操作。雖然這些類(lèi)的屬性分分別映射了數(shù)數(shù)據(jù)表的列,而而每一個(gè)對(duì)象象實(shí)例也恰恰恰對(duì)應(yīng)
于數(shù)數(shù)據(jù)表的每一行行,但這些實(shí)實(shí)體類(lèi)卻并不不具備對(duì)應(yīng)的數(shù)數(shù)據(jù)庫(kù)訪問(wèn)能能力。
由于于數(shù)據(jù)訪問(wèn)層和和業(yè)務(wù)邏輯層層都將對(duì)這些些數(shù)據(jù)實(shí)體進(jìn)行行操作,因此此程序集Moddel會(huì)被這兩兩層的
模塊塊所引用。
第二二類(lèi)數(shù)據(jù)庫(kù)對(duì)象象則是數(shù)據(jù)的的業(yè)務(wù)邏輯對(duì)對(duì)象。這里所指指的業(yè)務(wù)邏輯輯,并非業(yè)務(wù)邏邏輯層意義上上的領(lǐng)
域((domain)業(yè)業(yè)務(wù)邏輯(從從這個(gè)意義上,,我更傾向于于將業(yè)務(wù)邏輯層稱為“領(lǐng)域域邏輯層”),一般
意義義上說(shuō),這些業(yè)業(yè)務(wù)邏輯即為為基本的數(shù)據(jù)據(jù)庫(kù)操作,包括括Select,IInsert,Upd
date和Delete。
由于于這些業(yè)務(wù)邏輯輯對(duì)象,僅具具有行為而與與數(shù)據(jù)無(wú)關(guān),因因此它們均被被抽象為一個(gè)單獨(dú)的接口模模塊
IDAAL,例如數(shù)據(jù)據(jù)表Order對(duì)對(duì)應(yīng)的接口IOOrder:
將數(shù)數(shù)據(jù)實(shí)體與相關(guān)關(guān)的數(shù)據(jù)庫(kù)操操作分離出來(lái)來(lái),符合面向?qū)?duì)象的精神。首先,它體現(xiàn)了“職責(zé)分分離”
的原原則。將數(shù)據(jù)實(shí)實(shí)體與其行為為分開(kāi),使得兩兩者之間依賴賴減弱,當(dāng)數(shù)據(jù)行為發(fā)生改改變時(shí),并不不影響
Moddel模塊中的數(shù)據(jù)實(shí)體對(duì)象象,避免了因一一個(gè)類(lèi)職責(zé)過(guò)過(guò)多、過(guò)大,從從而導(dǎo)致該類(lèi)類(lèi)的引用者發(fā)生生“災(zāi)
難性性”的影響。其其次,它體現(xiàn)了“抽象”的精精神,或者說(shuō)說(shuō)是“面向接口口編程”的最佳佳體現(xiàn)。抽象的接
口模模塊IDAL,與與具體的數(shù)據(jù)據(jù)庫(kù)訪問(wèn)實(shí)現(xiàn)完完全隔離。這這種與實(shí)現(xiàn)無(wú)關(guān)的設(shè)計(jì),保保證了系統(tǒng)的的可擴(kuò)
展性性,同時(shí)也保證證了數(shù)據(jù)庫(kù)的的可移植性。在PetShopp中,可以支持SQLServver和Oraccle,
那么么它們具體的實(shí)實(shí)現(xiàn)就分別放放在兩個(gè)不同同的模塊SQLLServerDAL、OracleDAAL中。
以O(shè)Order為例,在SQLServverDAL、OrracleDAL兩兩個(gè)模塊中,有有不同的實(shí)現(xiàn)現(xiàn),但它們同時(shí)又
都實(shí)實(shí)現(xiàn)了IOrder接口,如圖圖:
從數(shù)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)來(lái)來(lái)看,PetShhop體現(xiàn)出了了沒(méi)有ORM框框架的臃腫與與丑陋。由于于要對(duì)數(shù)據(jù)表進(jìn)進(jìn)行
Inseert和Selecct操作,以SSQLServer為例,就使用用了SqlCommmand,SqqlParameter,
SqlDDataReader等對(duì)象,以以完成這些操作作。尤其復(fù)雜雜的是Parammeter的傳遞遞,在PetShhop
中,使用了大量的字符串常量量來(lái)保存參數(shù)數(shù)的名稱。此外外,PetShopp還專(zhuān)門(mén)為SSQLServer和
Oracle提供了抽抽象的Helpeer類(lèi),包裝了了一些常用的操作,如ExxecuteNonQQuery、
ExeecuteReader等方法。
在沒(méi)沒(méi)有ORM的情情況下,使用用Helper類(lèi)是是一個(gè)比較好好的策略,利用用它來(lái)完成數(shù)數(shù)據(jù)庫(kù)基本操操作的
封裝裝,可以減少很很多和數(shù)據(jù)庫(kù)庫(kù)操作有關(guān)的的代碼,這體現(xiàn)現(xiàn)了對(duì)象復(fù)用用的原則。PeetShop將這些
Helpper類(lèi)統(tǒng)一放放到DBUtilitty模塊中,不不同數(shù)據(jù)庫(kù)的的Helper類(lèi)暴暴露的方法基基本相同,只除除了
一些些特殊的要求,,例如Oraccle中處理boool類(lèi)型的方方式就和SQLServer不同同,從而專(zhuān)門(mén)門(mén)提供
了OOraBit和OrraBool方法。。此外,Helpper類(lèi)中的方方法均為stattic方法,以利利于調(diào)用。
OracleHelper的的類(lèi)圖如下:
對(duì)于于數(shù)據(jù)訪問(wèn)層來(lái)來(lái)說(shuō),最頭疼疼的是SQL語(yǔ)語(yǔ)句的處理。在早期的CSS結(jié)構(gòu)中,由由于未采用三層式
架構(gòu)構(gòu)設(shè)計(jì),數(shù)據(jù)訪訪問(wèn)層和業(yè)務(wù)務(wù)邏輯層是緊緊密糅合在一起起的,因此,SQL語(yǔ)句遍遍布與系統(tǒng)的每每一
個(gè)角角落。這給程序序的維護(hù)帶來(lái)極大的困難。。此外,由于OOracle使用的的是PL-SQLL,而SQLSeerver
和SSybase等使用用的是T-SQQL,兩者雖然都都遵循了標(biāo)準(zhǔn)準(zhǔn)SQL的語(yǔ)法法,但在很多細(xì)細(xì)節(jié)上仍有區(qū)區(qū)別,
如果果將SQL語(yǔ)句句大量的使用到程序中,無(wú)無(wú)疑為可能的的數(shù)據(jù)庫(kù)移植也也帶來(lái)了困難難。
最好好的方法是采用用存儲(chǔ)過(guò)程。這種方法使得程序更加整整潔,此外,由于存儲(chǔ)過(guò)程程可以以數(shù)據(jù)據(jù)庫(kù)腳
本的的形式存在,也也便于移植和和修改。但這種種方式仍然有有缺陷。一是存儲(chǔ)過(guò)程的測(cè)測(cè)試相對(duì)困難難。雖
然有有相應(yīng)的調(diào)試工工具,但比起起對(duì)代碼的調(diào)試試而言,仍然然比較復(fù)雜且且不方便。二是是對(duì)系統(tǒng)的更更新帶
來(lái)障障礙。如果數(shù)據(jù)據(jù)庫(kù)訪問(wèn)是由由程序完成,在.Net平臺(tái)臺(tái)下,我們僅需需要在修改程程序后,將重新編
譯的的程序集xcoppy到部署的服服務(wù)器上即可可。如果使用用了存儲(chǔ)過(guò)程,,出于安全的的考慮,必須須有專(zhuān)
門(mén)的的DBA重新運(yùn)運(yùn)行存儲(chǔ)過(guò)程程的腳本,部署署的方式受到到了限制。
我曾曾經(jīng)在一個(gè)項(xiàng)目中,利用一一個(gè)專(zhuān)門(mén)的表表來(lái)存放SQLL語(yǔ)句。如要使使用相關(guān)的SSQL語(yǔ)句,就就利
用關(guān)關(guān)鍵字搜索獲得得對(duì)應(yīng)語(yǔ)句。這種做法近似于存儲(chǔ)過(guò)程程的調(diào)用,但但卻避免了部署署上的問(wèn)題。然而
這種種方式卻在性能能上無(wú)法得到到保證。它僅適適合于SQL語(yǔ)語(yǔ)句較少的場(chǎng)場(chǎng)景。不過(guò),利利用良好的設(shè)設(shè)計(jì),
我們可以為各種業(yè)務(wù)提供不同的表來(lái)存放SQL語(yǔ)句。同樣的道理,這些SQL語(yǔ)句也可以存放
到XML文件中,更有利于系統(tǒng)的擴(kuò)展或修改。不過(guò)前提是,我們需要為它提供專(zhuān)門(mén)的SQL語(yǔ)
句管理工具。
SQL語(yǔ)句的使用無(wú)法避免,如何更好的應(yīng)用SQL語(yǔ)句也無(wú)定論,但有一個(gè)原則值得我們遵守,
就是“應(yīng)該盡量讓SQL語(yǔ)句盡存在于數(shù)據(jù)訪問(wèn)層的具體實(shí)現(xiàn)中”。
當(dāng)然,如果應(yīng)用ORM,那么一切就變得不同了。因?yàn)镺RM框架已經(jīng)為數(shù)據(jù)訪問(wèn)提供了基本的
Select,Insert,Update和Delete操作了。例如在NHibernate中,我們可以直接調(diào)用ISession
對(duì)象的Save方法,來(lái)Insert(或者說(shuō)是Create)一個(gè)數(shù)據(jù)實(shí)體對(duì)象:
publicvoidInsert(OrderInfoorder)
{
ISessions=Sessions.GetSession();
ITransactiontrans=null;
try
{
trans=s.BeginTransaction();
s.Save(order);
trans.Commit();
}
finally
{
s.Close();
}
}
沒(méi)有SQL語(yǔ)句,也沒(méi)有那些煩人的Parameters,甚至不需要專(zhuān)門(mén)去考慮事務(wù)。此外,這樣的
設(shè)計(jì),也是與數(shù)據(jù)庫(kù)無(wú)關(guān)的,NHibernate可以通過(guò)Dialect(方言)的機(jī)制支持不同的數(shù)據(jù)庫(kù)。
唯一要做的是,我們需要為OrderInfo定義hbm文件。
當(dāng)然,ORM框架并非是萬(wàn)能的,面對(duì)紛繁復(fù)雜的業(yè)務(wù)邏輯,它并不能完全消滅SQL語(yǔ)句,以
及替代復(fù)雜的數(shù)據(jù)庫(kù)訪問(wèn)邏輯,但它卻很好的體現(xiàn)了“80/20(或90/10)法則”(也被稱為“帕
累托法則”),也就是說(shuō):花比較少(10%-20%)的力氣就可以解決大部分(80%-90%)的
問(wèn)題,而要解決剩下的少部分問(wèn)題則需要多得多的努力。至少,那些在數(shù)據(jù)訪問(wèn)層中占據(jù)了絕大
部分的CRUD操作,通過(guò)利用ORM框架,我們就僅需要付出極少數(shù)時(shí)間和精力來(lái)解決它們了。
這無(wú)疑縮短了整個(gè)項(xiàng)目開(kāi)發(fā)的周期。
還是回到對(duì)PetShop的討論上來(lái)?,F(xiàn)在我們已經(jīng)有了數(shù)據(jù)實(shí)體,數(shù)據(jù)對(duì)象的抽象接口和實(shí)現(xiàn),
可以說(shuō)有關(guān)數(shù)據(jù)庫(kù)訪問(wèn)的主體就已經(jīng)完成了。留待我們的還有兩個(gè)問(wèn)題需要解決:
1、數(shù)據(jù)對(duì)象創(chuàng)建的管理
2、利于數(shù)據(jù)庫(kù)的移植
在PetShop中,要?jiǎng)?chuàng)建的數(shù)據(jù)對(duì)象包括Order,Product,Category,Inventory,Item。
在前面的設(shè)計(jì)中,這些對(duì)象已經(jīng)被抽象為對(duì)應(yīng)的接口,而其實(shí)現(xiàn)則根據(jù)數(shù)據(jù)庫(kù)的不同而有所不同。
也就是說(shuō),創(chuàng)建的對(duì)象有多種類(lèi)別,而每種類(lèi)別又有不同的實(shí)現(xiàn),這是典型的抽象工廠模式的應(yīng)
用場(chǎng)場(chǎng)景。而上面所所述的兩個(gè)問(wèn)問(wèn)題,也都可以以通過(guò)抽象工工廠模式來(lái)解解決。標(biāo)準(zhǔn)的抽抽象工廠模式式類(lèi)圖
如下下:
例如如,創(chuàng)建SQLLServer的Order對(duì)象如如下:
PetSShopFactorryfactory==newSQLSServerFactoory();
IOrdder=factoory.CreateOOrder();
要考考慮到數(shù)據(jù)庫(kù)的可移植性,則factory必須作為一個(gè)個(gè)全局變量,并在主程序運(yùn)行時(shí)被實(shí)例例化。
但這這樣的設(shè)計(jì)雖然然已經(jīng)達(dá)到了了“封裝變化””的目的,但在在創(chuàng)建PetShhopFactory對(duì)象時(shí),仍不不可
避免免的出現(xiàn)了具體體的類(lèi)SQLSServerFactoory,也即是是說(shuō),程序在這這個(gè)層面上產(chǎn)產(chǎn)生了與
SQLLServerFacttory的強(qiáng)依賴賴。一旦整個(gè)個(gè)系統(tǒng)要求支支持Oracle,那么還需要修改這行代碼碼為:
PetSShopFactorryfactory==neworaclleFactory();
修改改代碼的這種行行為顯然是不不可接受的。解決的辦法是是“依賴注入””?!耙蕾囎⑷肴搿钡墓δ芡ǔ3J怯?/p>
專(zhuān)門(mén)門(mén)的IoC容器器提供的,在Jaava平臺(tái)下,這樣的容器包包括Spring,PicoContainer等。而在在.Net
平臺(tái)臺(tái)下,最常見(jiàn)的的則是Sprinng.Net。不過(guò)過(guò),在PetShoop系統(tǒng)中,并并不需要專(zhuān)門(mén)的容器來(lái)實(shí)現(xiàn)現(xiàn)“依
賴注注入”,簡(jiǎn)單的的做法還是利用用配置文件和和反射功能來(lái)實(shí)現(xiàn)。也就是是說(shuō),我們可以在web.coonfig
文件件中,配置好具具體的Factoory對(duì)象的完完整的類(lèi)名。然然而,當(dāng)我們利利用配置文件件和反射功能能時(shí),
具體體工廠的創(chuàng)建就就顯得有些“畫(huà)蛇添足”了,我們完全可可以在配置文文件中,直接指指向具體的數(shù)數(shù)據(jù)庫(kù)
對(duì)象象實(shí)現(xiàn)類(lèi),例如如PetShop..SQLServerrDAL.IOrder。那么,抽象象工廠模式中中的相關(guān)工廠廠就可
以簡(jiǎn)簡(jiǎn)化為一個(gè)工廠廠類(lèi)了,所以以我將這種模式式稱之為“具有簡(jiǎn)單工廠特特質(zhì)的抽象工工廠模式”,其其類(lèi)圖
如下下:
DattaAccess類(lèi)完完全取代了前前面創(chuàng)建的工工廠類(lèi)體系,它它是一個(gè)seaaled類(lèi),其中中創(chuàng)建各種數(shù)數(shù)據(jù)對(duì)
象的的方法,均為靜靜態(tài)方法。之之所以能用這個(gè)個(gè)類(lèi)達(dá)到抽象象工廠的目的,是因?yàn)榕渲弥梦募头瓷渖涞倪\(yùn)
用,如下的代碼片片斷所示:
pubblicsealedcclassDataAAccess
{
//LLookuptheeDALimpleementationnweshoulddbeusing
pprivatestatticreadonlyystringpatth=
ConnfigurationMManager.ApppSettings[”WebDAL”]];
pprivatestatticreadonlyystringordderPath=
ConnfigurationMManager.ApppSettings[”O(jiān)rdersDAL”];
pubblicstaticPPetShop.IDAAL.IOrderCCreateOrdeer()
{
stringclassName==orderPathh+“.Orderr”;
return
(PettShop.IDALL.IOrder)Asssembly.Loaad(orderPaath).CreateIInstance(className);;
}}
}
在PPetShop中,這種依賴配配置文件和反射射創(chuàng)建對(duì)象的的方式極其常見(jiàn),包括IBLLStategy、
CaccheDependeencyFactoryy等等。這些些實(shí)現(xiàn)邏輯散布布于整個(gè)PettShop系統(tǒng)中中,在我看來(lái)來(lái),是
可以以在此基礎(chǔ)上進(jìn)進(jìn)行重構(gòu)的。也就是說(shuō),我們可以為整整個(gè)系統(tǒng)提供供類(lèi)似于“SerrviceLocatoor”的
實(shí)現(xiàn)現(xiàn):
pubblicstaticclassServiceeLocator
{
privvatestaticreadonlystringdalPath=
ConnfigurationMManager.ApppSettings[”WebDAL”]];
pprivatestatticreadonlyystringordderPath=
ConnfigurationMManager.ApppSettings[”O(jiān)rdersDAL”];
//……
privatestaticreadonlystringorderStategyPath=
ConfigurationManager.AppSettings[”O(jiān)rderStrategyAssembly”];
publicstaticobjectLocateDALObject(stringclassName)
{
stringfullPath=dalPath+“.”+className;
returnAssembly.Load(dalPath).CreateInstance(fullPath);
}
publicstaticobjectLocateDALOrderObject(stringclassName)
{
stringfullPath=orderPath+“.”+className;
returnAssembly.Load(orderPath).CreateInstance(fullPath);
}
publicstaticobjectLocateOrderStrategyObject(stringclassName)
{
stringfullPath=orderStategyPath+“.”+className;
returnAssembly.Load(orderStategyPath).CreateInstance(fullPath);
}
//……
}
那么和所謂“依賴注入”相關(guān)的代碼都可以利用ServiceLocator來(lái)完成。例如類(lèi)DataAccess
就可以簡(jiǎn)化為:
publicsealedclassDataAccess
{
publicstaticPetShop.IDAL.IOrderCreateOrder()
{
return(PetShop.IDAL.IOrder)ServiceLocator.
LocateDALOrderObject(”O(jiān)rder”);
}
}
通過(guò)ServiceLocator,將所有與配置文件相關(guān)的namespace值統(tǒng)一管理起來(lái),這有利于各種
動(dòng)態(tài)創(chuàng)建對(duì)象的管理和未來(lái)的維護(hù)。
petshop4.0詳解之三(PetShop數(shù)據(jù)訪問(wèn)層之消息處理)
在進(jìn)行系統(tǒng)設(shè)計(jì)時(shí),除了對(duì)安全、事務(wù)等問(wèn)題給與足夠的重視外,性能也是一個(gè)不可避免的問(wèn)題
所在,尤其是一個(gè)B/S結(jié)構(gòu)的軟件系統(tǒng),必須充分地考慮訪問(wèn)量、數(shù)據(jù)流量、服務(wù)器負(fù)荷的問(wèn)
題。解決性能的瓶頸,除了對(duì)硬件系統(tǒng)進(jìn)行升級(jí)外,軟件設(shè)計(jì)的合理性尤為重要。
在前面我曾提到,分層式結(jié)構(gòu)設(shè)計(jì)可能會(huì)在一定程度上影響數(shù)據(jù)訪問(wèn)的性能,然而與它給設(shè)計(jì)人
員帶來(lái)的好處相比,幾乎可以忽略。要提供整個(gè)系統(tǒng)的性能,還可以從數(shù)據(jù)庫(kù)的優(yōu)化著手,例如
連接池的使用、建立索引、優(yōu)化查詢策略等等,例如在PetShop中就利用了數(shù)據(jù)庫(kù)的Cache,
對(duì)于數(shù)據(jù)量較大的訂單數(shù)據(jù),則利用分庫(kù)的方式為其單獨(dú)建立了Order和Inventory數(shù)據(jù)庫(kù)。
而在軟件設(shè)計(jì)上,比較有用的方式是利用多線程與異步處理方式。
在PPetShop4.0中,使用了MicrosoftMMessagingQQueue(MSMMQ)技術(shù)來(lái)完完成異步處理,利
用消消息隊(duì)列臨時(shí)存存放要插入的的數(shù)據(jù),使得得數(shù)據(jù)訪問(wèn)因?yàn)闉椴恍枰L問(wèn)問(wèn)數(shù)據(jù)庫(kù)從而提供了訪問(wèn)性性能,
至于于隊(duì)列中的數(shù)據(jù)據(jù),則等待系系統(tǒng)空閑的時(shí)時(shí)候再進(jìn)行處理理,將其最終終插入到數(shù)據(jù)庫(kù)中。
PetSShop4.0中的消息處理,主要分為如如下幾部分:消消息接口IMeessaging、消消息工廠
MesssagingFacttory、MSMQQ實(shí)現(xiàn)MSMMQMessaginng以及數(shù)據(jù)后后臺(tái)處理應(yīng)用用程序
OrderProcessoor。
從模模塊化分上,PPetShop自始始自終地履行行了“面向接口口設(shè)計(jì)”的原則則,將消息處處理的接口與實(shí)實(shí)現(xiàn)
分開(kāi)開(kāi),并通過(guò)工廠廠模式封裝消消息實(shí)現(xiàn)對(duì)象象的創(chuàng)建,以達(dá)達(dá)到松散耦合合的目的。
由于于在PetShopp中僅對(duì)訂單單的處理使用了了異步處理方方式,因此在消消息接口IMessaging中中,僅
定義義了一個(gè)IOrdder接口,其其類(lèi)圖如下:
在對(duì)對(duì)消息接口的實(shí)實(shí)現(xiàn)中,考慮慮到未來(lái)的擴(kuò)擴(kuò)展中會(huì)有其他他的數(shù)據(jù)對(duì)象象會(huì)使用MSMMQ,因此定義了
一個(gè)個(gè)Queue的基基類(lèi),實(shí)現(xiàn)消消息Receive和Send的基基本操作:
pubblicvirtualoobjectReceeive()
{
try
{
using(Messagemmessage=qqueue.Receeive(timeouut,transacttionType))
returrnmessagee;
}
catch(MeessageQueuueExceptionnmqex)
{
if(mqeex.MessageeQueueErrorCode==MessageQuueueErrorCCode.IOTimeeout)
throwwnewTimeeoutException();
thrrow;
}
}
pubblicvirtualvvoidSend(oobjectmsg)
{
queue.Sennd(msg,traansactionTyype);
}
其中中queue對(duì)象象是Systemm.Messagingg.MessageQQueue類(lèi)型,作為存放數(shù)據(jù)據(jù)的隊(duì)列。MSSMQ
隊(duì)列列是一個(gè)可持久久的隊(duì)列,因因此不必?fù)?dān)心心用戶不間斷地地下訂單會(huì)導(dǎo)導(dǎo)致訂單數(shù)據(jù)的丟失。在
PetSShopQueuee設(shè)置了timeout值,OrderProcesssor會(huì)根據(jù)timeout值定定期掃描隊(duì)列中的
訂單單數(shù)據(jù)。
MSMMQMessaging模塊中,Order對(duì)象實(shí)實(shí)現(xiàn)了IMesssaging模塊塊中定義的接口IOrder,同時(shí)
它還還繼承了基類(lèi)PetShopQuueue,其定義義如下:
pubblicclassorrder:PetShoopQueue,PPetShop.IMeessaging.IOOrder
方法的實(shí)現(xiàn)代碼如下:
publicneworderInfoReceive()
{
//ThismethodinvolvesindistributedtransactionandneedAutomatic
Transactiontype
base.transactionType=MessageQueueTransactionType.Automatic;
return(OrderInfo)((Message)base.Receive()).Body;
}
publicorderInfoReceive(inttimeout)
{
base.timeout=TimeSpan.FromSeconds(Convert.ToDouble(timeout));
returnReceive();
}
publicvoidSend(OrderInfoorderMessage)
{
//Thismethoddoesnotinvolveindistributedtransactionandoptimizes
performanceusingSingletype
base.transactionType=MessageQueueTransactionType.Single;
base.Send(orderMessage);
}
所以,最后的類(lèi)圖應(yīng)該如下:
注意意在Order類(lèi)類(lèi)的Receive()方法中,是是用new關(guān)鍵鍵字而不是ooverride關(guān)鍵鍵字來(lái)重寫(xiě)其其父類(lèi)
PetSShopQueuee的Receivee()虛方法。因因此,如果是是實(shí)例化如下的對(duì)象,將會(huì)會(huì)調(diào)用
PetSShopQueuee的Receivee()方法,而不不是子類(lèi)Ordder的Receive()方法:
PetSShopQueueequeue=nneworder();
queeue.Receivee();
從設(shè)設(shè)計(jì)上來(lái)看,由由于PetShoop采用“面向接口設(shè)計(jì)”的的原則,如果我我們要?jiǎng)?chuàng)建OOrder對(duì)象,應(yīng)該
采用用如下的方式:
IOrdderorder==neworder();
ordeer.Receive(();
考慮慮到IOrder的的實(shí)現(xiàn)有可能能的變化,PettShop仍然利利用了工廠模模式,將IOrdder對(duì)象的創(chuàng)創(chuàng)建用
專(zhuān)門(mén)門(mén)的工廠模塊進(jìn)進(jìn)行了封裝:
在類(lèi)QueueAccess中,通過(guò)CreateOrder()方法利用反射技術(shù)創(chuàng)建正確的IOrder類(lèi)型對(duì)象:
publicstaticPetShop.IMessaging.IOrderCreateOrder()
{
stringclassName=path+".Order";
return
PetShop.IMessaging.IOrder)Assembly.Load(path).CreateInstance(className);
}
path的值通過(guò)配置文件獲?。?/p>
privatestaticreadonlystringpath=
ConfigurationManager.AppSettings["OrderMessaging"];
而配置文件中,OrderMessaging的值設(shè)置如下:
<addkey="OrderMessaging"value="PetShop.MSMQMessaging"/>
之所以利用工廠模式來(lái)負(fù)責(zé)對(duì)象的創(chuàng)建,是便于在業(yè)務(wù)層中對(duì)其調(diào)用,例如在BLL模塊中
OrderAsynchronous類(lèi):
publicclassorderAsynchronous:IOrderStrategy
{
privatestaticreadonlyPetShop.IMessaging.IOrderasynchOrder=
PetShop.MessagingFactory.QueueAccess.CreateOrder();
publicvoidInsert(PetShop.Model.OrderInfoorder)
{
asynchOrder.Send(order);
}
}
一旦IOrder接口的實(shí)現(xiàn)發(fā)生變化,這種實(shí)現(xiàn)方式就可以使得客戶僅需要修改配置文件,而不需
要修改代碼,如此就可以避免程序集的重新編譯和部署,使得系統(tǒng)能夠靈活應(yīng)對(duì)需求的改變。例
如定義一個(gè)實(shí)現(xiàn)IOrder接口的SpecialOrder,則可以新增一個(gè)模塊,如
PetShop.SpecialMSMQMessaging,而類(lèi)名則仍然為Order,那么此時(shí)我們僅需要修改配置
文件中OrderMessaging的值即可:
<addkey="OrderMessaging"value="PetShop.SpecialMSMQMessaging"/>
OrderProcessor是一個(gè)控制臺(tái)應(yīng)用程序,不過(guò)可以根據(jù)需求將其設(shè)計(jì)為WindowsService。
它的目的就是接收消息隊(duì)列中的訂單數(shù)據(jù),然后將其插入到Order和Inventory數(shù)據(jù)庫(kù)中。它
利用了多線程技術(shù),以達(dá)到提高系統(tǒng)性能的目的。
在OrderProcessor應(yīng)用程序中,主函數(shù)Main用于控制線程,而核心的執(zhí)行任務(wù)則由方法
ProcessOrders()實(shí)現(xiàn):
privatestaticvoidProcessOrders()
{
//thetransactiontimeoutshouldbelongenoughtohandleallofordersinthe
batch
TimeSpantsTimeout=
TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout*batchSize));
orderorder=neworder();
while(true)
{
//queuetimeoutvariables
TimeSpandatetimeStarting=newTimeSpan(DateTime.Now.Ticks);
doubleelapsedTime=0;
intprocessedItems=0;
ArrayListqueueOrders=newArrayList();
using(TransactionScopets=new
TransactionScope(TransactionScopeOption.Required,tsTimeout))
{
//Receivetheordersfromthequeue
for(intj=0;j<batchSize;j++)
{
try
{
//onlyreceivemorequeuedordersifthereisenoughtime
if((elapsedTime+queueTimeout+transactionTimeout)<
tsTimeout.TotalSeconds)
{
queueOrders.Add(order.ReceiveFromQueue(queueTimeout));
}
else
{
j=batchSize;//exitloop
}
//updateelapsedtime
elapsedTime=new
TimeSpan(DateTime.Now.Ticks).TotalSeconds-datetimeStarting.TotalSeconds;
}
catch(TimeoutException)
{
//exitloopbecausenomoremessagesarewaiting
j=batchSize;
}
}
//processthequeuedorders
for(intk=0;k<queueOrders.Count;k++)
{
order.Insert((OrderInfo)queueOrders[k]);
processedItems++;
totalOrdersProcessed++;
}
//bbatchcompleteorMSMMQreceivetimedout
ts.CComplete());
}
Consoole.WriteLinne("(ThreaddId"+
Threead.CurrenntThread.MaanagedThreeadId+")bbatchfinishhed,"+proocessedItemms+
"iteems,in"+elapsedTimme.ToStringg()+"secconds.");
}
}}
首先先,它會(huì)通過(guò)PPetShop.BLLL.Order類(lèi)的的公共方法RReceiveFrommQueue()來(lái)來(lái)獲取消息隊(duì)隊(duì)列中
的訂訂單數(shù)據(jù),并將將其放入到一一個(gè)ArrayLisst對(duì)象中,然然而再調(diào)用PPetShop.BLL.Order類(lèi)的的
Inseert方法將其其插入到Ordeer和Inventtory數(shù)據(jù)庫(kù)中中。
在PPetShop.BLLL.Order類(lèi)中中,并不是直接執(zhí)行插入訂訂單的操作,而是調(diào)用了IOrderStrattegy
接口口的Insert()方法:
pubblicvoidInssert(OrderInfoorder)
{
///Callcreditcardproccesor
PProcessCredditCard(ordder);
///Inserttheorder(a))synchrounouslybaseddonconfigguration
oorderInserttStrategy.Innsert(orderr);
}
在這這里,運(yùn)用了一一個(gè)策略模式式,類(lèi)圖如下下所示:
在PPetShop.BLLL.Order類(lèi)中中,仍然利用用配置文件來(lái)動(dòng)動(dòng)態(tài)創(chuàng)建IOrrderStategyy對(duì)象:
privvatestaticrreadonlyPeetShop.IBLLStrategy.IIOrderStrattegyorderInsertStrateegy
=LoadInsertSStrategy();
privvatestaticPPetShop.IBLLStrategyy.IOrderStraategyLoadInsertStrattegy()
{
///Lookupwhichstrattegytouseefromconfiigfile
sstringpath=ConfigurrationManaager.AppSetttings["OrdderStrategyyAssembly"];
sstringclasssName=CoonfigurationnManager.AAppSettings["OrderStrategyClasss"];
///Usingtheeevidencegiveninthheconfigfileeloadtheaappropriateeassemblyand
classs
rreturn
(PettShop.IBLLLStrategy.IOOrderStrateegy)Assembly.Load(paath).CreateeInstance(cclass
Namme);
}
由于于OrderProccessor是一個(gè)個(gè)單獨(dú)的應(yīng)用用程序,因此它它使用的配置置文件與PetSShop不同,是存
放在在應(yīng)用程序的App.config文件中,在該該文件中,對(duì)對(duì)IOrderStaategy的配置置為:
<adddkey="OrrderStrateggyAssemblyy"value="PPetShop.BLL"/>
<adddkey="OrrderStrateggyClass"value="PetShhop.BLL.OrdderSynchroonous"/>
因此此,以異步方式式插入訂單的的流程如下圖圖所示:
MicrrosoftMesssagingQueuue(MSMQ)技技術(shù)除用于異異步處理以外,它主要還是是一種分布式式處理
技術(shù)術(shù)。分布式處理理中,一個(gè)重重要的技術(shù)要素素就是有關(guān)消消息的處理,而在Systemm.Messaginng命
名空空間中,已經(jīng)提提供了Messsage類(lèi),可以以用于承載消消息的傳遞,前前提上消息的的發(fā)送方與接接收方
在數(shù)數(shù)據(jù)定義上應(yīng)有有統(tǒng)一的接口口規(guī)范。
MSMMQ在分布式式處理的運(yùn)用,在我參與的的項(xiàng)目中已經(jīng)經(jīng)有了實(shí)現(xiàn)。在在為一個(gè)汽車(chē)車(chē)制造商開(kāi)發(fā)一個(gè)
大型型系統(tǒng)時(shí),分銷(xiāo)銷(xiāo)商Dealerr作為.Net客客戶端,需要將將數(shù)據(jù)傳遞到到管理中心,并且該數(shù)據(jù)將將被
Oracle的EBS(E-BusinesssSystem)使使用。由于分銷(xiāo)銷(xiāo)商管理系統(tǒng)統(tǒng)(DMS)采用用的是C/S結(jié)結(jié)構(gòu),
數(shù)據(jù)據(jù)庫(kù)為SQLSServer,而汽汽車(chē)制造商管理理中心的EBS數(shù)據(jù)庫(kù)為OOracle。這里里就涉及到兩兩個(gè)系
統(tǒng)之之間數(shù)據(jù)的傳遞遞。
實(shí)現(xiàn)現(xiàn)架構(gòu)如下:
首先Dealerr的數(shù)據(jù)通過(guò)過(guò)MSMQ傳遞遞到MSMQSServer,此時(shí)時(shí)可以將數(shù)據(jù)據(jù)插入到SQLL
Servver數(shù)據(jù)庫(kù)中中,同時(shí)利用FTP將數(shù)據(jù)傳送到專(zhuān)門(mén)的的文件服務(wù)器器上。然后利用用IBM的EAAI技
術(shù)((企業(yè)應(yīng)用集成成,EnterprriseApplicaationItegraation)定期將將文件服務(wù)器器中的文件,利利用
接口口規(guī)范寫(xiě)入到EAI數(shù)據(jù)庫(kù)服服務(wù)器中,并并最終寫(xiě)道EBBS的Oracle數(shù)據(jù)庫(kù)中。
上述述架構(gòu)是一個(gè)典典型的分布式式處理結(jié)構(gòu),而技術(shù)實(shí)現(xiàn)的的核心就是MMSMQ和EAAI。由于我們們已經(jīng)
定義義了統(tǒng)一的接口規(guī)范,在通通過(guò)消息隊(duì)列列形成文件后,,此時(shí)的數(shù)據(jù)據(jù)就已經(jīng)與平臺(tái)無(wú)關(guān)了,使使得
在.NNet平臺(tái)下的的分銷(xiāo)商管理系系統(tǒng)能夠與OOracle的EBBS集成起來(lái),完成數(shù)據(jù)的的處理。
pettshop4.0詳詳解之四(PeetShop之AASP.NET緩緩存)
如果果對(duì)微型計(jì)算機(jī)機(jī)硬件系統(tǒng)有有足夠的了解解,那么我們對(duì)于Cache這個(gè)名詞一定定是耳熟能詳詳?shù)摹?/p>
在CCPU以及主板板的芯片中,都引入了這種種名為高速緩緩沖存儲(chǔ)器(CCache)的技技術(shù)。因?yàn)镃aache
的存存取速度比內(nèi)存存快,因而引引入Cache能能夠有效的解解決CPU與內(nèi)內(nèi)存之間的速速度不匹配問(wèn)題題。
硬件件系統(tǒng)可以利用用Cache存存儲(chǔ)CPU訪問(wèn)問(wèn)概率高的那些些數(shù)據(jù),當(dāng)CCPU需要訪問(wèn)問(wèn)這些數(shù)據(jù)時(shí)時(shí),可
以直直接從Cachee中讀取,而不不必訪問(wèn)存取取速度相對(duì)較慢的內(nèi)存,從從而提高了CPU的工作效效率。
軟件件設(shè)計(jì)借鑒了硬硬件設(shè)計(jì)中引引入緩存的機(jī)機(jī)制以改善整個(gè)個(gè)系統(tǒng)的性能能,尤其是對(duì)于于一個(gè)數(shù)據(jù)庫(kù)庫(kù)驅(qū)動(dòng)
的WWeb應(yīng)用程序序而言,緩存存的利用是不可可或缺的,畢畢竟,數(shù)據(jù)庫(kù)庫(kù)查詢可能是整整個(gè)Web站點(diǎn)中
調(diào)用用最頻繁但同時(shí)時(shí)又是執(zhí)行最最緩慢的操作作之一,我們不不能被它老邁邁的雙腿拖緩我們前進(jìn)的征征程。
緩存存機(jī)制正是解決決這一缺陷的的加速器。
4.1ASP.NETT緩存概述
作為為.Net框架下下開(kāi)發(fā)Web應(yīng)應(yīng)用程序的主主打產(chǎn)品,ASSP.NET充分考考慮了緩存機(jī)機(jī)制。通過(guò)某某種方
法,將系統(tǒng)需要的的數(shù)據(jù)對(duì)象、Web頁(yè)面存存儲(chǔ)在內(nèi)存中,,使得Web站站點(diǎn)在需要獲獲取這些數(shù)據(jù)據(jù)時(shí),
不需需要經(jīng)過(guò)繁瑣的數(shù)據(jù)庫(kù)連接接、查詢和復(fù)雜的邏輯運(yùn)算算,就可以“觸觸手可及”,如如“探囊取物””般容
易而而快速,從而提提高整個(gè)Weeb系統(tǒng)的性能能。
ASPP.NET提供了了兩種基本的的緩存機(jī)制來(lái)提提供緩存功能能。一種是應(yīng)用用程序緩存,它允許開(kāi)發(fā)發(fā)者將
程序序生成的數(shù)據(jù)或或報(bào)表業(yè)務(wù)對(duì)對(duì)象放入緩存存中。另外一種種緩存機(jī)制是是頁(yè)輸出緩存,,利用它,可可以直
接獲獲取存放在緩存存中的頁(yè)面,而不需要經(jīng)經(jīng)過(guò)繁雜的對(duì)該該頁(yè)面的再次次處理。
應(yīng)用程序緩存其實(shí)現(xiàn)原理說(shuō)來(lái)平淡無(wú)奇,僅僅是通過(guò)ASP.NET管理內(nèi)存中的緩存空間。放入緩
存中的應(yīng)用程序數(shù)據(jù)對(duì)象,以鍵/值對(duì)的方式存儲(chǔ),這便于用戶在訪問(wèn)緩存中的數(shù)據(jù)項(xiàng)時(shí),可以
根據(jù)key值判斷該項(xiàng)是否存在緩存中。
放入在緩存中的數(shù)據(jù)對(duì)象其生命周期是受到限制的,即使在整個(gè)應(yīng)用程序的生命周期里,也不能
保證該數(shù)據(jù)對(duì)象一直有效。ASP.NET可以對(duì)應(yīng)用程序緩存進(jìn)行管理,例如當(dāng)數(shù)據(jù)項(xiàng)無(wú)效、過(guò)期
或內(nèi)存不足時(shí)移除它們。此外,調(diào)用者還可以通過(guò)CacheItemRemovedCallback委托,定義
回調(diào)方法使得數(shù)據(jù)項(xiàng)被移除時(shí)能夠通知用戶。
在.NetFramework中,應(yīng)用程序緩存通過(guò)System.Web.Caching.Cache類(lèi)實(shí)現(xiàn)。它是一個(gè)
密封類(lèi),不能被繼承。對(duì)于每一個(gè)應(yīng)用程序域,都要?jiǎng)?chuàng)建一個(gè)Cache類(lèi)的實(shí)例,其生命周期與
應(yīng)用程序域的生命周期保持一致。我們可以利用Add或Insert方法,將數(shù)據(jù)項(xiàng)添加到應(yīng)用程序
緩存中,如下所示:
Cache["First"]="FirstItem";
Cache.Insert("Second","SecondItem");
我們還可以為應(yīng)用程序緩存添加依賴項(xiàng),使得依賴項(xiàng)發(fā)生更改時(shí),該數(shù)據(jù)項(xiàng)能夠從緩存中移除:
string[]dependencies={"Second"};
Cache.Insert("Third","ThirdItem",
newSystem.Web.Caching.CacheDependency(null,dependencies));
與之對(duì)應(yīng)的是緩存中數(shù)據(jù)項(xiàng)的移除。前面提到ASP.NET可以自動(dòng)管理緩存中項(xiàng)的移除,但我們
也可以通過(guò)代碼編寫(xiě)的方式顯式的移除相關(guān)的數(shù)據(jù)項(xiàng):
Cache.Remove("First");
相對(duì)于應(yīng)用程序緩存而言,頁(yè)輸出緩存的應(yīng)用更為廣泛。它可以通過(guò)內(nèi)存將處理后的ASP.NET
頁(yè)面存儲(chǔ)起來(lái),當(dāng)客戶端再一次訪問(wèn)該頁(yè)面時(shí),可以省去頁(yè)面處理的過(guò)程,從而提高頁(yè)面訪問(wèn)的
性能,以及Web服務(wù)器的吞吐量。例如,在一個(gè)電子商務(wù)網(wǎng)站里,用戶需要經(jīng)常查詢商品信息,
這個(gè)過(guò)程會(huì)涉及到數(shù)據(jù)庫(kù)訪問(wèn)以及搜索條件的匹配,在數(shù)據(jù)量較大的情況下,如此的搜索過(guò)程是
較為耗時(shí)的。此時(shí),利用頁(yè)輸出緩存就可以將第一次搜索得到的查詢結(jié)果頁(yè)存儲(chǔ)在緩存中。當(dāng)用
戶第二次查詢時(shí),就可以省去數(shù)據(jù)查詢的過(guò)程,減少頁(yè)面的響應(yīng)時(shí)間。
頁(yè)輸出緩存分為整頁(yè)緩存和部分頁(yè)緩存。我們可以通過(guò)@OutputCache指令完成對(duì)Web頁(yè)面
的輸出緩存。它主要包含兩個(gè)參數(shù):Duration和VaryByParam。Duration參數(shù)用于設(shè)置頁(yè)
面或控件進(jìn)行緩存的時(shí)間,其單位為秒。如下的設(shè)置表示緩存在60秒內(nèi)有效:
<%@OutputCacheDuration=“60“VaryByParam=“none“%>
只要沒(méi)有超過(guò)Duration設(shè)置的期限值,當(dāng)用戶訪問(wèn)相同的頁(yè)面或控件時(shí),就可以直接在緩存中
獲取。
使用VaryByParam參數(shù)可以根據(jù)設(shè)置的參數(shù)值建立不同的緩存。例如在一個(gè)輸出天氣預(yù)報(bào)結(jié)
果的頁(yè)面中,如果需要為一個(gè)ID為txtCity的TextBox控件建立緩存,其值將顯示某城市的
氣溫,那么我們可以進(jìn)行如下的設(shè)置:
<%@OutputCacheDuration=”60”VaryByParam=”txtCity”%>
如此一來(lái),ASP.NET會(huì)對(duì)txtCity控件的值進(jìn)行判斷,只有輸入的值與緩存值相同,才從緩存
中取出相應(yīng)的值。這就有效地避免了因?yàn)橹档牟煌鴮?dǎo)致輸出錯(cuò)誤的數(shù)據(jù)。
利用緩存的機(jī)制對(duì)性能的提升非常明顯。通過(guò)ACT(ApplicationCenterTest)的測(cè)試,可以
發(fā)現(xiàn)設(shè)置緩存后執(zhí)行的性能比未設(shè)置緩存時(shí)的性能足足提高三倍多。
引入緩存看來(lái)是提高性能的“完美”解決方案,然而“金無(wú)足赤,人無(wú)完人”,緩存機(jī)制也有缺點(diǎn),
那就是數(shù)據(jù)過(guò)期的問(wèn)題。一旦應(yīng)用程序數(shù)據(jù)或者頁(yè)面結(jié)果值發(fā)生的改變,那么在緩存有效期范圍
內(nèi),你所獲得的結(jié)果將是過(guò)期的、不準(zhǔn)確的數(shù)據(jù)。我們可以想一想股票系統(tǒng)利用緩存所帶來(lái)的災(zāi)
難,當(dāng)你利用錯(cuò)誤過(guò)期的數(shù)據(jù)去分析股市的風(fēng)云變幻時(shí),你會(huì)發(fā)現(xiàn)獲得的結(jié)果真可以說(shuō)是“失之
毫厘,謬以千里”,看似大好的局面就會(huì)像美麗的泡沫一樣,用針一戳,轉(zhuǎn)眼就消失得無(wú)影無(wú)蹤。
那么我們是否應(yīng)該為了追求高性能,而不顧所謂“數(shù)據(jù)過(guò)期”所帶來(lái)的隱患呢?顯然,在類(lèi)似于股
票系統(tǒng)這種數(shù)據(jù)更新頻繁的特定場(chǎng)景下,數(shù)據(jù)過(guò)期的糟糕表現(xiàn)甚至比低效的性能更讓人難以接受。
故而,我們需要在性能與數(shù)據(jù)正確性間作出權(quán)衡。所幸的是,.NetFramework2.0引入了一
種新的緩存機(jī)制,它為我們的“魚(yú)與熊掌兼得”帶來(lái)了技術(shù)上的可行性。
.Net2.0引入的自定義緩存依賴項(xiàng),特別是基于MS-SQLServer的SqlCacheDependency
特性,使得我們可以避免“數(shù)據(jù)過(guò)期”的問(wèn)題,它能夠根據(jù)數(shù)據(jù)庫(kù)中相應(yīng)數(shù)據(jù)的變化,通知緩存,
并移除那些過(guò)期的數(shù)據(jù)。事實(shí)上,在PetShop4.0中,就充分地利用了SqlCacheDependency
特性。
4.2SqlCacheDependency特性
SqlCacheDependency特性實(shí)際上是通過(guò)System.Web.Caching.SqlCacheDependency
類(lèi)來(lái)體現(xiàn)的。通過(guò)該類(lèi),可以在所有支持的SQLServer版本(7.0,2000,2005)上監(jiān)視特
定的SQLServer數(shù)據(jù)庫(kù)表,并創(chuàng)建依賴于該表以及表中數(shù)據(jù)行的緩存項(xiàng)。當(dāng)數(shù)據(jù)表或表中特
定行的數(shù)據(jù)發(fā)生更改時(shí),具有依賴項(xiàng)的數(shù)據(jù)項(xiàng)就會(huì)失效,并自動(dòng)從Cache中刪除該項(xiàng),從而保
證了緩存中不再保留過(guò)期的數(shù)據(jù)。
由于版本的原因,SQLServer2005完全支持SqlCacheDependency特性,但對(duì)于SQL
Server7.0和SQLServer2000而言,就沒(méi)有如此幸運(yùn)了。畢竟這些產(chǎn)品出現(xiàn)在.Net
Framework2.0之前,因此它并沒(méi)有實(shí)現(xiàn)自動(dòng)監(jiān)視數(shù)據(jù)表數(shù)據(jù)變化,通知ASP.NET的功能。
解決的辦法就是利用輪詢機(jī)制,通過(guò)ASP.NET進(jìn)程內(nèi)的一個(gè)線程以指定的時(shí)間間隔輪詢SQL
Server數(shù)據(jù)庫(kù),以跟蹤數(shù)據(jù)的變化情況。
要使得7.0或者2000版本的SQLServer支持SqlCacheDependency特性,需要對(duì)數(shù)據(jù)庫(kù)
服務(wù)器執(zhí)行相關(guān)的配置步驟。有兩種方法配置SQLServer:使用aspnet_regsql命令行工具,
或者使用SqlCacheDependencyAdmin類(lèi)。
4.2.1利用aspnet_regsql工具
aspnet_regsql工具位于Windows\Microsoft.NET\Framework\[版本]文件夾中。如果直接
雙擊該工具的執(zhí)行文件,會(huì)彈出一個(gè)向?qū)?duì)話框,提示我們完成相應(yīng)的操作:
圖4-1aaspnet_reggsql工具
如圖圖4-1所示中的提示信息,說(shuō)明該向?qū)е髦饕糜谂渲弥肧QLServeer數(shù)據(jù)庫(kù),如如membershhip,
proffiles等信息,如果要配置SqlCacheDependency,則需要以命命令行的方式執(zhí)執(zhí)行。以PetSShop
4.0為例,數(shù)據(jù)庫(kù)庫(kù)名為MSPeetShop4,則則命令為:
aspnet_regsql-Slocalhosst-E-dMSSPetShop4-ed
以下下是該工具的命命令參數(shù)說(shuō)明明:
-?顯示該工具的的幫助功能;
-S后接的參數(shù)為為數(shù)據(jù)庫(kù)服務(wù)務(wù)器的名稱或或者IP地址;
-U后接的參數(shù)為為數(shù)據(jù)庫(kù)的登登陸用戶名;
-P后接的參數(shù)為為數(shù)據(jù)庫(kù)的登登陸密碼;
-E當(dāng)使用winddows集成驗(yàn)驗(yàn)證時(shí),使用該該功能;
-d后接參數(shù)為對(duì)對(duì)哪一個(gè)數(shù)據(jù)據(jù)庫(kù)采用SqlCCacheDepeendency功能能;
-t后接參數(shù)為對(duì)對(duì)哪一個(gè)表采采用SqlCachheDependenncy功能;
-ed允許對(duì)數(shù)據(jù)據(jù)庫(kù)使用SqlCCacheDepeendency功能能;
-dd禁止對(duì)數(shù)據(jù)據(jù)庫(kù)采用SqlCCacheDepeendency功能能;
-et允許對(duì)數(shù)據(jù)據(jù)表采用SqlCCacheDepenndency功能能;
-dt禁止對(duì)數(shù)據(jù)據(jù)表采用SqlCCacheDependency功能能;
-lt列出當(dāng)前數(shù)據(jù)據(jù)庫(kù)中有哪些些表已經(jīng)采用sqlcachedeependency功能。
以上上面的命令為例例,說(shuō)明將對(duì)名為MSPetShop4的數(shù)據(jù)庫(kù)采用SqqlCacheDeppendency功功能,
且SSQLServer采用了winddows集成驗(yàn)驗(yàn)證方式。我們們還可以對(duì)相相關(guān)的數(shù)據(jù)表執(zhí)行
aspnet_regsql命令,如:
aspnet_regsql-Slocalhosst-E-dMSSPetShop4-tItem-et
aspnet_regsql-Slocalhosst-E-dMSSPetShop4-tProduct-et
aspnet_regsql-Slocalhosst-E-dMSSPetShop4-tCategoryy-et
當(dāng)執(zhí)執(zhí)行上述的四條條命令后,aaspnet_regssql工具會(huì)在在MSPetShop4數(shù)據(jù)庫(kù)中建立一個(gè)名為為
AsppNet_SqlCacheTablesFForChangeNNotification的新數(shù)據(jù)庫(kù)表表。該數(shù)據(jù)表表包含三個(gè)字段。
字段段tableNamee記錄要追蹤蹤的數(shù)據(jù)表的名稱,例如在在PetShop4.0中,要記記錄的數(shù)據(jù)表表就包
括CCategory、Item和Prodduct。notificcationCreatted字段記錄錄開(kāi)始追蹤的時(shí)間。changgeId
作為為一個(gè)類(lèi)型為int的字段,用于記錄數(shù)據(jù)據(jù)表數(shù)據(jù)發(fā)生生變化的次數(shù)數(shù)。如圖4-2所示:
圖4-2AspNett_SqlCacheeTablesForCChangeNotiification數(shù)據(jù)據(jù)表
除此此之外,執(zhí)行該該命令還會(huì)為為MSPetShoop4數(shù)據(jù)庫(kù)添添加一組存儲(chǔ)儲(chǔ)過(guò)程,為ASSP.NET提供供查詢
追蹤蹤的數(shù)據(jù)表的情情況,同時(shí)還還將為使用了SqlCacheDDependencyy的表添加觸觸發(fā)器,分別對(duì)對(duì)應(yīng)
Inseert、Updatee、Delete等等與數(shù)據(jù)更改改相關(guān)的操作。。例如Product數(shù)據(jù)表的的觸發(fā)器:
CreateTRIGGEERdbo.[Prooduct_AspNNet_SqlCacheNotification_Triggerr]ON[Prodduct]
FFORInsert,,Update,DDeleteASBBEGIN
SSETNOCOUUNTON
EEXECdbo.AAspNet_SqlCacheUpdaateChangeIIdStoredProocedureN'PProduct'
ENDD
其中中,AspNet_SSqlCacheUppdateChanggeIdStoredProcedure即是工具添加加的一組存儲(chǔ)儲(chǔ)過(guò)程
中的的一個(gè)。當(dāng)對(duì)Product數(shù)據(jù)據(jù)表執(zhí)行Inssert、Updatee或Delete等操作時(shí),就就會(huì)激活觸發(fā)發(fā)器,
然后后執(zhí)行AspNeet_SqlCacheUpdateChhangeIdStoredProceduure存儲(chǔ)過(guò)程程。其執(zhí)行的過(guò)過(guò)程
就是是修改AspNeet_SqlCacheTablesForrChangeNottification數(shù)據(jù)表的channgeId字段值值:
CreatePROCEDUREdbo.AAspNet_SqqlCacheUpdateChangeIdStoredPrrocedure
@tabbleNameNVVARCHAR(4450)
AS
BEGIN
Updaatedbo.AsppNet_SqlCacheTablesFForChangeNNotificationWITH
(ROOWLOCK)SETchangeIId=changeId+1
WherretableNamme=@tableName
END
GO
4.2.2利用SqlCacheDepeendencyAdmin類(lèi)
我們們也可以利用編編程的方式來(lái)來(lái)來(lái)管理數(shù)據(jù)據(jù)庫(kù)對(duì)SqlCaccheDependdency特性的的使用。該類(lèi)包包含
了五五個(gè)重要的方法法:
DisableNootifications為特定數(shù)據(jù)庫(kù)禁用SqqlCacheDeppendency
對(duì)象更改通知
DisableTableForNotifications為數(shù)據(jù)庫(kù)中的特定表禁用
SqlCacheDependency對(duì)象更改通知
EnableNotifications為特定數(shù)據(jù)庫(kù)啟用SqlCacheDependency
對(duì)象更改通知
EnableTableForNotifications為數(shù)據(jù)庫(kù)中的特定表啟用
SqlCacheDependency對(duì)象更改通知
GetTablesEnabledForNotifications返回啟用了SqlCacheDependency對(duì)象更
改通知的所有表的列表
表4-1SqlCacheDependencyAdmin類(lèi)的主要方法
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 山西省渾源縣第七中學(xué)2024-2025學(xué)年高三第一次調(diào)查研究考試(4月)化學(xué)試題含解析
- 浙江商業(yè)職業(yè)技術(shù)學(xué)院《體育產(chǎn)業(yè)經(jīng)營(yíng)管理》2023-2024學(xué)年第二學(xué)期期末試卷
- 懷化學(xué)院《中國(guó)古代物質(zhì)文化與審美風(fēng)尚》2023-2024學(xué)年第二學(xué)期期末試卷
- 重慶市忠縣達(dá)標(biāo)名校2025屆初三2月七校聯(lián)考數(shù)學(xué)試題含解析
- 山東菏澤市曹縣重點(diǎn)中學(xué)2024-2025學(xué)年初三下學(xué)期第六次周練化學(xué)試題文試題含解析
- 山東省濟(jì)南市章丘區(qū)重點(diǎn)中學(xué)2024-2025學(xué)年初三第一次強(qiáng)化訓(xùn)練英語(yǔ)試題含答案
- 遼寧對(duì)外經(jīng)貿(mào)學(xué)院《外景采集與創(chuàng)作》2023-2024學(xué)年第二學(xué)期期末試卷
- 長(zhǎng)春大學(xué)旅游學(xué)院《醫(yī)學(xué)影像診斷學(xué)上》2023-2024學(xué)年第一學(xué)期期末試卷
- 天津市和平區(qū)2025年高三第二次(4月)適應(yīng)性測(cè)試生物試題試卷含解析
- 廈門(mén)理工學(xué)院《食品安全檢測(cè)技術(shù)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2024年江蘇省無(wú)錫市天一實(shí)驗(yàn)學(xué)校中考英語(yǔ)押題試卷含答案
- DB3305-T 57-2018幸福鄰里中心建設(shè)與服務(wù)管理規(guī)范
- AIGC基礎(chǔ)與應(yīng)用全套教學(xué)課件
- 9.1.3二項(xiàng)分布(解析版)
- 神經(jīng)生長(zhǎng)因子在神經(jīng)退行性疾病中的作用
- 國(guó)有企業(yè)采購(gòu)管理規(guī)范 T/CFLP 0027-2020
- 《災(zāi)害風(fēng)險(xiǎn)管理》 課件 第6、7章 災(zāi)害風(fēng)險(xiǎn)分析與評(píng)估、災(zāi)害發(fā)生前的風(fēng)險(xiǎn)管理
- 2024年內(nèi)蒙古中考地理生物試卷
- 緩和醫(yī)療-以死觀生的生活智慧智慧樹(shù)知到期末考試答案章節(jié)答案2024年嘉興大學(xué)
- 小學(xué)畢業(yè)考試數(shù)學(xué)試卷附答案【b卷】
- 2024年江蘇省泰州市興化市中考二模數(shù)學(xué)試題
評(píng)論
0/150
提交評(píng)論