Microsoft PetShop 3.0 設計與實(shí)現--數據訪(fǎng)問(wèn)層
最近對多層設計實(shí)現和.Net產(chǎn)生了興趣,從而研究了一下比較著(zhù)名的多層范例程序――PetShop,現在的版本是3.0,和以前的版本從設計上已有一定的區別,應該是和Java的Petshop設計相當。 關(guān)于一些Microsoft PetShop的來(lái)由、如何安裝,所表現業(yè)務(wù)流程,數據庫表結構等基本的信息的資料請大家參考下面文章 http://msdn.microsoft.com/library/en-us/dnbda/html/bdasamppet.asp 另外建議先看一下這篇文章: http://msdn.microsoft.com/library/en-us/dnbda/html/petshop3x.asp (如有格式問(wèn)題本文可到http://www.surfsky.com/bbs/myfiles/MSPetShop3.0%20Report.doc 下載) 本文將以設計和實(shí)現緊密結合的方式來(lái)分析,這也是我們廣大實(shí)踐型的軟件開(kāi)發(fā)人員的風(fēng)格。先看一下設計圖和具體實(shí)現VS.NET工程的表格。 MSPetShop 3.0 系統結構圖: 從圖中可以看到系統大體分為Presentation,Business Logic,Data Access 三層,每層中又有子層。每層(也包括子層)各司其職,又互相協(xié)作,本文順序以此圖為準,從下到上分析。 對應上圖,具體的.NET Project實(shí)現列表(借用MS文章中的列表不用翻譯了吧) Project Purpose BLL Home for business logic components ConfigTool Administration application used to encrypt connection strings and create event log source DALFactory Classes used to determine which database access assembly to load IDAL Set of interfaces which need to be implemented by each DAL implementation Model Thin data classes or business entities OracleDAL Oracle specific implementation of the Pet Shop DAL which uses the IDAL interfaces Post-Build Project to run post compile actions such as adding assemblies to the GAC or COM+ Pre-Build Project to remove assemblies from the GAC or unregister assemblies from COM+ SQLServerDAL Microsoft SQL Server specific implementation of the Pet Shop DAL which uses the IDAL interfaces Utility Set of helper classes including a wrapper for the DPAPI Web Web pages and controls Solution Items Miscellaneous items used to build the application such as Pet Shop.snk key file used to sign application assemblies 另外我寫(xiě)這篇文章時(shí)是一邊看源碼一邊寫(xiě),所以建意大家最好安裝一個(gè)Petshop3,因為時(shí)間倉促,水平有限,如我有不對之處請給我發(fā)Email更正。email:cocoboy79@163.com qq:364941 首先我們來(lái)看一下DAL層。 一:Data Access Layer: 1 PetShop.Utility如下圖:(上表中Utility為其實(shí)現工程) 正如上表所描述,這個(gè)名字空間有兩個(gè)類(lèi)一個(gè)是ConnectionInfo用于加密解密數據庫連接信息,另一個(gè)DataProtector調用了Crypt32.dll和kernel32.dll實(shí)現一些底層數據安全操作,這個(gè)類(lèi)要在下面的PetShop.XXXDAL名字空間中調用,可見(jiàn)Petshop.Utility只是起到的是數據訪(fǎng)問(wèn)輔助工具的作用。 2 PetShop.SQLServerDAL ――系統結構圖中DAL層中的SqlServer DAL子層實(shí)現 SqlHelper類(lèi)實(shí)際上是封裝了關(guān)于此系統中數據庫操作訪(fǎng)問(wèn)的一些常用功能,其中它還會(huì )調用上面的PetShop.Utility中的ConectionInfo類(lèi)方法加密解密連接字符串,如:ConnectionInfo.DecryptDBConnectionString方法。SqlHelper類(lèi)是基于Microsoft Data Access Application Block for .NET。這個(gè)東西是用來(lái)幫助用戶(hù)更好的在.NET的訪(fǎng)問(wèn)數據。如MS一段話(huà):Are you involved in the design and development of data access code for .NET-based applications? Have you ever felt that you write the same data access code again and again? Have you wrapped data access code in helper functions that let you call a stored procedure in one line? If so, the Microsoft® Data Access Application Block for .NET is for you。其實(shí)可以自已寫(xiě)一個(gè)類(lèi)似SqlHelper的東西,以實(shí)現一般化的對數據庫的操作,以在各項目中重用,當然也可以使用現在的MS為你做好的這個(gè)SqlHelper或是Microsoft® Data Access Application Block for .NET,避免不同項目中總是寫(xiě)同樣的重復的數據庫訪(fǎng)問(wèn)程序。有時(shí)間最好還是看一下SqlHelper的具體程序實(shí)現思路以及所提到的那個(gè)Microsoft Data Access Application Block for .NET。不過(guò)這里我們的SqlHelper應該只是部分實(shí)現。更全面信息請參看:http://msdn.microsoft.com/library/en-us/dnbda/html/daab-rm.asp Account類(lèi)對用戶(hù)帳戶(hù)進(jìn)行操作如Insert,Update,SignIn,其中這些對數據庫的操作,使用了上面的SqlHelper類(lèi)來(lái)實(shí)現。另外Inventory和Order,Product,Profile和Account類(lèi)的都是同樣對數據庫相關(guān)表進(jìn)行操作,程序風(fēng)格一致,這些類(lèi)中對數據庫的操作都是通過(guò)此名字空間下的SqlHelper類(lèi)進(jìn)行的,例如,下面語(yǔ)句: private const string SQL_INSERT_SIGNON = "INSERT INTO SignOn VALUES (@UserId, @Password)"; private const string PARM_USER_ID = "@UserId"; private const string PARM_PASSWORD = "@Password"; 來(lái)定義一個(gè)sql語(yǔ)句,以及聲明其中可變參數,然后像下面這樣用SqlHelper類(lèi)的合適的方法執行: SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_SIGNON, signOnParms); 最后在SQLHelper.ExecuteNonQuery實(shí)現中,再調用ado.net中的相關(guān)類(lèi)最終執行對數據庫的操作,可見(jiàn)SqlHelper在這里又封裝了一下ado.net相關(guān)類(lèi)以?xún)?yōu)化數據操作。正如SqlHelper.cs中注釋提示:The SqlHelper class is intended to encapsulate high performance, scalable best practices for common uses of SqlClient.下面是SqlHelper. ExecuteNonQuery的實(shí)現內容: public static int ExecuteNonQuery(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms) { //注:運行時(shí)cmdText的實(shí)參就是SQL_INSERT_SIGNON SqlCommand cmd = new SqlCommand(); using (SqlConnection conn = new SqlConnection(connString)) { PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms); int val = cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); return val; } } 另外Inventory和Order,Product,Profile和Account類(lèi)的聲明都是像public class Account : IAccount這樣實(shí)現某個(gè)相關(guān)的接口,像IAccount這樣的接口是在PetShop.IDAL中聲明的,見(jiàn)后面介紹。 3 PetShop.OracleDAL ―――系統結構圖中 DAL層的OracleDAL子層實(shí)現 個(gè)人認為結構應該同上面的PetShop. SQLServerDAL,另外SqlHelper變成了OraHelper,在OraHelper中當然具體實(shí)現了對特定的Oracle數據庫的聯(lián)接操作,看一下源程序很明顯原來(lái)的 SqlCommand cmd = new SqlCommand(); 變成了OracleCommand cmd = new OracleCommand();。 注意一下:在系統結構圖中的DAL層還有兩個(gè)XXX DAAB的子層,它們對應的實(shí)現在哪里呢? 下面對應一下: 以下是左邊是圖中 DataAccessLayer的各部分,右邊是具體實(shí)現所在名字空間或類(lèi) SqlServer DAL――PetShop.SQLServerDAL名字空間 Sql DAAB――PetShop.SqlServerDal.SqlHelper類(lèi) Oracle DAL――PetShop.OracleDAL名字空間 Oracle DAAB――PetShop.OracleDAL.OraHelper類(lèi) 4 PetShop.IDAL 數據訪(fǎng)問(wèn)接口――對應系統結構圖中DAL Interface 接口是一種系列‘功能’的聲明或名單,接口沒(méi)有實(shí)現細節,如下接口IAccount定義也可以看出IAccount只有聲明: using System; using PetShop.Model; namespace PetShop.IDAL { // Inteface for the Account DAL public interface IAccount { // Authenticate a user AccountInfo SignIn(string userId, string password); /// Get a user‘s address stored in the database AddressInfo GetAddress(string userId); /// Insert an account into the database void Insert(AccountInfo account); /// Update an account in the database void Update(AccountInfo Account); } } 您只需要調用接口,而不用管接口是如何實(shí)現的那么接口沒(méi)有實(shí)現,調用它有什么用?實(shí)際上接口的實(shí)現是由某個(gè)類(lèi)來(lái)做的,那么這里的IAccount接口是由PetShop.SqlServerDAL.Account類(lèi)或是PetShop.OracleDAL.Account類(lèi)來(lái)實(shí)現的,從他們的定義可以看到: public class Account : IAccount {…….} 為什么是兩個(gè)類(lèi)都實(shí)現同一接口又是‘或’呢?因為這里使用接口的目的就是為了統一‘外觀(guān)’,當上層BLL層調用此接口方法時(shí)不用知道這個(gè)接口由哪個(gè)類(lèi)實(shí)現的。那誰(shuí)來(lái)確定使用哪個(gè)類(lèi)的實(shí)現?請再看下面。 (PetShop.IDAL下的其它接口和IAccount一樣,故在此略過(guò)。) 5 PetShop.DALFactory 數據訪(fǎng)問(wèn)工廠(chǎng) 工廠(chǎng)模式是設計模式的一種,以我理解就像Factory這個(gè)詞一樣,對于用戶(hù)來(lái)說(shuō),工廠(chǎng)里產(chǎn)品如何生產(chǎn)的你不用知道,你只要去用工廠(chǎng)里生產(chǎn)出來(lái)的東西就可以了。MSPetShop3.0用工廠(chǎng)模式來(lái)實(shí)現了對SqlServer和Oracle數據庫訪(fǎng)問(wèn)的操作,而用戶(hù)(business Logic Layer)不用知道也不用關(guān)心后臺用的是哪一種數據庫,它只要用接口就行了,接口中定義了要用的方法,當調用接口時(shí)會(huì )根據具體的情況再去調用底層數據訪(fǎng)問(wèn)操作。而現在這個(gè)DALFactory就是關(guān)鍵,當BLL層要操作數據庫時(shí),DALFactory會(huì )根據具體情況再去使用本文上面介紹的SqlServerDAL和OracleDAL中的一個(gè)。這樣系統上層只管調用,而下層來(lái)實(shí)現細節,上級只管發(fā)號施令,下級去干活。對于上層來(lái)說(shuō)實(shí)現細節被隱藏了。 那么DALFactory是如何決定應該用SqlServerDAL還是用OracleDAL的呢?我們接著(zhù)分析。 以下是PetShop.DALFactory.Account類(lèi)的實(shí)現: namespace PetShop.DALFactory { /// <summary> /// Factory implementaion for the Account DAL object /// </summary> public class Account { public static PetShop.IDAL.IAccount Create() //<<<<ß----這里返回接口 { /// Look up the DAL implementation we should be using string path = System.Configuration.ConfigurationSettings.AppSettings["WebDAL"]; string className = path + ".Account"; // Using the evidence given in the config file load the appropriate assembly and class return (PetShop.IDAL.IAccount) Assembly.Load(path).CreateInstance(className); } } } 以下則是web.config中<appSettings>節點(diǎn)中的一部分: <add key="WebDAL" value="PetShop.SQLServerDAL" /> <add key="OrdersDAL" value="PetShop.SQLServerDAL" /> <add key="Event Log Source" value=".NET Pet Shop" /> 上面的Create()方法返回IAccount接口,用System.Configuration.ConfigurationSettings.AppSettings["WebDAL"];則可以得到Web.config的<appsettings>節點(diǎn)中的關(guān)于系統中應該使用哪個(gè)數據訪(fǎng)問(wèn)層(SqlserverDAL還是OracleDAL)的信息。因為我在安裝PetShop3.0時(shí)選擇的是Sqlserver所以在此是:value="PetShop.SQLServerDAL",如果用的是Oracle那就是value="PetShop.OracleDAL" 了吧!而且這個(gè)文件也應該是可以更改的。接下來(lái)className=path+”.Account”返回的應該是PetShop.SQLServerDAL.Account,然后再用Assembly.Load加載PetShop.SQLServerDAL.dll,同時(shí)創(chuàng )建PetShop.SQLServerDAL.Account的實(shí)例,并以接口(PetShop.IDAL.IAccount)類(lèi)型返回。這樣BLL調用IAccount接口時(shí)就會(huì )用PetShop.SQLServerDAL.Account類(lèi)的實(shí)現代碼。(回上面第4再看一下) 看!這樣根據系統當前Web.config文件的配置描述(這也應該是系統運行時(shí)實(shí)際的配置),BLL層只要像下面這樣: // Get an instance of the account DAL using the DALFactory IAccount dal = PetShop.DALFactory.Account.Create(); AccountInfo account = dal.SignIn(userId, password);//<<ß----看看上面第4點(diǎn)的IAccount接口 就可以直接調用接口方法通過(guò)下層DAL層操作數據庫了(在此具體為用戶(hù)賬號相關(guān)操作),而B(niǎo)LL層并不用知道應該通過(guò)SqlserverDAL還是OracleDAL訪(fǎng)問(wèn)數據庫,這由都DAL Factory決定,你用的是什么數據庫以及底層細節,更不用BLL知道,這樣做的好處是對于BLL層以及更上層的程序不會(huì )或很少機率會(huì )因為底層程序變動(dòng)影響,因為BLL層中調用接口就行了,只要那個(gè)接口定義沒(méi)變,一切仍然OK. 6 PetShop.ConfigTool 首先在..\Microsoft\PetShop\ConfigTool\中有一個(gè)app.config文件,看一下其中內容,分別定義了兩種數據庫的聯(lián)接字符串,在app.config中有一行 <add key="WebConfigFileLocation" value="Web\Web.config" /> 則標識出給asp.net程序使用的web.config配置文件的相對位置。然后看一下PetShopConnectionString的EncryptConnectionString方法的源碼,這個(gè)類(lèi)中先是從當前目錄的app.config文件中讀出web.config文件的位置,如下: public static readonly string CONFIGFILE = ConfigurationSettings.AppSettings["WebConfigFileLocation"]; 然后語(yǔ)句 XmlDocument doc = new XmlDocument(); doc.Load(CONFIGFILE); 加載Web.config文件,最后將加密的連接字符串寫(xiě)入Web.config對應的XML節點(diǎn)中。以供Asp.net應用程序使用。其中的加密還是使用PetShop.Utility。 而ConfigConsole,調用PetShopConnectionString類(lèi)EncryptConnectionString執行對聯(lián)接字符串進(jìn)行加密。另外PetShopEventLog類(lèi)也是在ConfigConsole中使用的。用于記錄程序日志。 所以在最后部署時(shí)Web.config的連接字符串是加密的。 7 PetShop.Model 業(yè)務(wù)實(shí)體模型 http://www.surfsky.com/bbs/myfiles/7.bmp 這個(gè)本來(lái)想在分析BLL層時(shí)再說(shuō),但是在SqlServerDAL和OracleDAL中都使用了這些Model,無(wú)論怎么樣,上層的程序執行最終結果都是要操作數據庫,而數據庫是關(guān)系型,不是面向對象的,那就得把平面的‘表’結合業(yè)務(wù)規則抽象成類(lèi),這樣想辦法讓上層(BLL及以上)以為自已在操作類(lèi)而不是數據庫表,從而使‘它們’感覺(jué)沒(méi)有數據庫的存在,上層只管面向對象編程就可以了。類(lèi)似現在所說(shuō)的O-R MAPPING,但O-R MAPPING比這種簡(jiǎn)單的數據到對象的持久化要復雜。據說(shuō)可以在表結構有變化的情況下,上層應用程序代碼不用更改,只要改O-R MAPPING的相關(guān)設置就可以了。 上面第2節中已看到Petshop的SqlServerDal和OracleDal中定義的sql語(yǔ)句,然后根據上層的調用,把sql語(yǔ)句傳給SqlHelper執行,再來(lái)看看SqlServerDal的一段程序: private void SetAccountParameters(SqlParameter[] parms, AccountInfo acc) { parms[0].Value = acc.Email; parms[1].Value = acc.Address.FirstName; parms[2].Value = acc.Address.LastName; parms[3].Value = acc.Address.Address1; parms[4].Value = acc.Address.Address2; parms[5].Value = acc.Address.City; parms[6].Value = acc.Address.State; parms[7].Value = acc.Address.Zip; parms[8].Value = acc.Address.Country; parms[9].Value = acc.Address.Phone; parms[10].Value = acc.UserId; } parms[x]就是那些有聲明為常量的有參數的Sql語(yǔ)句中的參數,這里用Model中的AccountInfo的各Field和這些參數Mapping。所以對于BLL層,只知道這些Model就可以了,反正最后在SqlServerDAL或是OracleDAL中Model的成員們會(huì )Mapping到參數中以存取數據庫(為什么不直接Mapping到數據集的字段呢?我想應該是因為這里數據庫操作直接給SqlHeper的原因,不然就沒(méi)必要用SqlHelper了。于是才又多了圖中的xxx DAAB層,這樣Mapping給參數再傳給DAAB來(lái)處理) Data Access Layer總結: DAL完成數據庫訪(fǎng)問(wèn)任務(wù),上層(BLL)直需調用接口即可,不用知道具體訪(fǎng)問(wèn)細節,用Factory模式來(lái)實(shí)現,使用運行時(shí)讀取web.config的方法來(lái)得到連接配置信息,最后選用SqlServerDAL或OracleDAL之一的相對具體子層,同時(shí)使用SqlHelper或OraHelper之一來(lái)完成數據庫操作。





聯(lián)系客服