对比.Net PetShop和Duwamish来探讨Ado.Net的数据库编程模式
生活随笔
收集整理的這篇文章主要介紹了
对比.Net PetShop和Duwamish来探讨Ado.Net的数据库编程模式
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
作者:盧彥
.NET PetShop和Duwamish簡單介紹 相信大家一定聽說過有名的"寵物店大戰(zhàn)",沒錯,本文的主角之一就是獲勝方.NET PetShop,微軟號稱以27倍的速度和1/4的代碼量遙遙領(lǐng)先于基于J2EE的PetStore寵物商店。雖然SUN也曾對此抱怨過不滿,指責(zé)此"大戰(zhàn)"有水分,不過無論如何,.NET PetShop絕對是一個經(jīng)典的.NET實例教程,至少為我們提供了一條趕超J2EE的“捷徑” :),它的下載地址是:http://www.gotdotnet.com/team/compare
.NET PetShop寵物網(wǎng)上商店首頁
而Duwamish則是一個外表簡單,內(nèi)部卻極其復(fù)雜的一個網(wǎng)上書店的.NET完整應(yīng)用范例,作為一個微軟官方的Sample,它同時提供了C#和VB.NET兩種語言版本,并且還附上了大量詳盡的中文資料,如果打印出來,實在是居家旅行,臨睡入廁必備之物。什么?您沒聽說過?呵呵,如果您裝了Visual Studio .NET的話,它就在您的硬盤上靜靜的躺著呢,不過還沒有被安裝,您可以在您的VS.NET 的Enterprise Samples目錄下找到并安裝它,例如:C:/Program Files/Microsoft Visual Studio .NET/Enterprise Samples/Duwamish 7.0 CS。 Duwamish網(wǎng)上電子書店首頁
結(jié)構(gòu)簡述 兩家商店都采用了n層應(yīng)用結(jié)構(gòu)(毫無疑問,n層結(jié)構(gòu)的應(yīng)用架構(gòu)應(yīng)該絕對是您開發(fā).NET應(yīng)用的首選,哪怕您只想做一個網(wǎng)頁計數(shù)器),不同的是,PetShop采用的是最常見的三層應(yīng)用結(jié)構(gòu),分別為表示層,中間層和數(shù)據(jù)層。而Duwamish則采用的是一個四層應(yīng)用結(jié)構(gòu),并使用不同的項目分隔開,分別為表示層,業(yè)務(wù)外觀層,業(yè)務(wù)規(guī)則層和數(shù)據(jù)層。至于這兩種結(jié)構(gòu)分別有什么優(yōu)點和缺點,以及為什么要這么分層,我們不進(jìn)行詳細(xì)討論,因為本文的重點不在于此。我們主要分析的是他們的數(shù)據(jù)庫編程的模式。 Duwamish數(shù)據(jù)訪問剖析 首先,我們來看看Duwamish書店,它采用的是DataAdapter和DataSet配合的數(shù)據(jù)存儲模式,所不同的是,它對DataSet進(jìn)行子類化擴展作為數(shù)據(jù)載體,也就是采用定制的DataSet來進(jìn)行層間的數(shù)據(jù)傳輸,下面是一個定制的DataSet示例:
.NET PetShop和Duwamish簡單介紹 相信大家一定聽說過有名的"寵物店大戰(zhàn)",沒錯,本文的主角之一就是獲勝方.NET PetShop,微軟號稱以27倍的速度和1/4的代碼量遙遙領(lǐng)先于基于J2EE的PetStore寵物商店。雖然SUN也曾對此抱怨過不滿,指責(zé)此"大戰(zhàn)"有水分,不過無論如何,.NET PetShop絕對是一個經(jīng)典的.NET實例教程,至少為我們提供了一條趕超J2EE的“捷徑” :),它的下載地址是:http://www.gotdotnet.com/team/compare
.NET PetShop寵物網(wǎng)上商店首頁
而Duwamish則是一個外表簡單,內(nèi)部卻極其復(fù)雜的一個網(wǎng)上書店的.NET完整應(yīng)用范例,作為一個微軟官方的Sample,它同時提供了C#和VB.NET兩種語言版本,并且還附上了大量詳盡的中文資料,如果打印出來,實在是居家旅行,臨睡入廁必備之物。什么?您沒聽說過?呵呵,如果您裝了Visual Studio .NET的話,它就在您的硬盤上靜靜的躺著呢,不過還沒有被安裝,您可以在您的VS.NET 的Enterprise Samples目錄下找到并安裝它,例如:C:/Program Files/Microsoft Visual Studio .NET/Enterprise Samples/Duwamish 7.0 CS。 Duwamish網(wǎng)上電子書店首頁
結(jié)構(gòu)簡述 兩家商店都采用了n層應(yīng)用結(jié)構(gòu)(毫無疑問,n層結(jié)構(gòu)的應(yīng)用架構(gòu)應(yīng)該絕對是您開發(fā).NET應(yīng)用的首選,哪怕您只想做一個網(wǎng)頁計數(shù)器),不同的是,PetShop采用的是最常見的三層應(yīng)用結(jié)構(gòu),分別為表示層,中間層和數(shù)據(jù)層。而Duwamish則采用的是一個四層應(yīng)用結(jié)構(gòu),并使用不同的項目分隔開,分別為表示層,業(yè)務(wù)外觀層,業(yè)務(wù)規(guī)則層和數(shù)據(jù)層。至于這兩種結(jié)構(gòu)分別有什么優(yōu)點和缺點,以及為什么要這么分層,我們不進(jìn)行詳細(xì)討論,因為本文的重點不在于此。我們主要分析的是他們的數(shù)據(jù)庫編程的模式。 Duwamish數(shù)據(jù)訪問剖析 首先,我們來看看Duwamish書店,它采用的是DataAdapter和DataSet配合的數(shù)據(jù)存儲模式,所不同的是,它對DataSet進(jìn)行子類化擴展作為數(shù)據(jù)載體,也就是采用定制的DataSet來進(jìn)行層間的數(shù)據(jù)傳輸,下面是一個定制的DataSet示例:
public class BookData : DataSet
{public BookData(){//// Create the tables in the dataset//BuildDataTables();}private void BuildDataTables(){//// Create the Books table//DataTable table = new DataTable(BOOKS_TABLE);DataColumnCollection columns = table.Columns;columns.Add(PKID_FIELD, typeof(System.Int32));columns.Add(TYPE_ID_FIELD, typeof(System.Int32));columns.Add(PUBLISHER_ID_FIELD, typeof(System.Int32));columns.Add(PUBLICATION_YEAR_FIELD, typeof(System.Int16));columns.Add(ISBN_FIELD, typeof(System.String));columns.Add(IMAGE_FILE_SPEC_FIELD, typeof(System.String));columns.Add(TITLE_FIELD, typeof(System.String));columns.Add(DESCRIPTION_FIELD, typeof(System.String));columns.Add(UNIT_PRICE_FIELD, typeof(System.Decimal));columns.Add(UNIT_COST_FIELD, typeof(System.Decimal));columns.Add(ITEM_TYPE_FIELD, typeof(System.String));columns.Add(PUBLISHER_NAME_FIELD, typeof(System.String));this.Tables.Add(table);}………
} 我們可以看到它有一個BuildDataTables方法,并且在構(gòu)造函數(shù)中調(diào)用,這樣,定制的Books表就和這個DataSet捆綁在一起了,省得以后還要進(jìn)行Column Mapping,這真是個好主意,我怎么就沒有想到呢? :) 解決了數(shù)據(jù)結(jié)構(gòu),接下來看看數(shù)據(jù)層的代碼實現(xiàn),在Duwamish中,數(shù)據(jù)層中有5個類,分別是Books,Categories,Customers和Orders,每個類分別只負(fù)責(zé)有關(guān)數(shù)據(jù)的存取。下面是其中一個類的示例代碼: private SqlDataAdapter dsCommand;
public BookData GetBookById(int bookId)
{return FillBookData("GetBookById", "@BookId", bookId.ToString());
}
private BookData FillBookData(String commandText, String paramName, String paramValue)
{if (dsCommand == null ){throw new System.ObjectDisposedException( GetType().FullName );} BookData data = new BookData();SqlCommand command = dsCommand.SelectCommand;command.CommandText = commandText;command.CommandType = CommandType.StoredProcedure; // use stored proc for perfSqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);param.Value = paramValue;command.Parameters.Add(param); dsCommand.Fill(data);return data;
} 這里就是數(shù)據(jù)層的代碼了,我們在這里可以看到Duwamish采用了DataAdapter來將數(shù)據(jù)填充到定制的DataSet中,然后返回該DataSet。我感到很奇怪的是在數(shù)據(jù)存取層中竟然可以看到GetBookById這樣具體的數(shù)據(jù)存取方法,雖然最后還是有一個抽象出來的FillBookData方法,但是上面還有三層啊,底層都做到這份上了,那上層都做些什么呢?答案是數(shù)據(jù)檢查,上層基本上都在做一些很嚴(yán)密的數(shù)據(jù)合法性校驗(當(dāng)然也會包括一些比較復(fù)雜的事務(wù)邏輯,但是并不多),示例代碼如下: public CustomerData GetCustomerByEmail(String emailAddress, String password)
{//// Check preconditions//ApplicationAssert.CheckCondition(emailAddress != String.Empty, "Email address is required",ApplicationAssert.LineNumber);ApplicationAssert.CheckCondition(password != String.Empty, "Password is required", ApplicationAssert.LineNumber);//// Get the customer dataSet//CustomerData dataSet;using (DataAccess.Customers customersDataAccess = new DataAccess.Customers()){dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);}// // Verify the customer's password//DataRowCollection rows = dataSet.Tables[CustomerData.CUSTOMERS_TABLE].Rows;if ( ( rows.Count == 1 ) && rows[0][CustomerData.PASSWORD_FIELD].Equals(password) ){return dataSet;}else{ return null;}
} 在這個方法中,真正進(jìn)行數(shù)據(jù)存取的實際上只有 dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);這么一句,是直接調(diào)用的數(shù)據(jù)層。其它都是在進(jìn)行合法性校驗,我們可以感悟到,進(jìn)行一個真正的企業(yè)級開發(fā)需要考慮的系統(tǒng)健壯性有多么重要。 .NET PetShop數(shù)據(jù)訪問剖析 OK,Duwamish看完了,下面我們來看看PetShop的數(shù)據(jù)訪問機制。 PetShop只有一個項目,它采用的分層辦法是將中間層和數(shù)據(jù)層都寫成cs文件放在Components目錄里,其中數(shù)據(jù)層就是一個名為Database的類,它封裝了所有對數(shù)據(jù)庫的底層操作。下面是示例代碼段:
public void RunProc(string procName, out SqlDataReader dataReader)
{SqlCommand cmd = CreateCommand(procName, null);dataReader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
} 我們看到了一個跟Duwamish截然不同的另一種數(shù)據(jù)訪問方式,它將所有的數(shù)據(jù)訪問方法抽象出來做成一個RunProc方法,至于返回數(shù)據(jù)呢,呵呵,它有點偷懶,直接返回一個DataReader給你,你自己去讀吧。還記得Duwamish采用的層間數(shù)據(jù)傳輸載體是什么嗎?對了,是DataSet,它被數(shù)據(jù)層填充后返回給了中間層。但是這里,數(shù)據(jù)層和傳輸層的數(shù)據(jù)傳輸載體變成了DataReader,實際上,還不能稱它為數(shù)據(jù)載體,因為數(shù)據(jù)還沒開始讀呢,在這里,DataReader的作用和指針有點類似,也許我們應(yīng)該稱它為“數(shù)據(jù)引用”:) 接著往下看,DataReader被怎么“處理”的: public ProductResults[] GetList(string catid, int currentPage, int pageSize, ref int numResults)
{numResults = 0;int index=0;SqlDataReader reader = GetList(catid);ProductResults[] results = new ProductResults[pageSize];// now loop through the list and pull out items of the specified pageint start = (int)((currentPage - 1) * pageSize);if (start <= 0) start = 1;// skip for (int i = 0; i < start - 1; i++) {if (reader.Read()) numResults++;}if (start > 1) reader.Read();// read the data we are interested inwhile (reader.Read()) {if (index < pageSize) {results[index] = new ProductResults();results[index].productid = reader.GetString(0);results[index].name = reader.GetString(1);index++;}numResults++; }reader.Close();// see if need to redim arrayif (index == pageSize)return results;else {// not a full page, redim arrayProductResults[] results2 = new ProductResults[index];Array.Copy(results, results2, index);return results2;}
}
注意到currentPage和pageSize了嗎?原來在這里就進(jìn)行了數(shù)據(jù)分頁,只返回滿足需要的最少的數(shù)據(jù)量,而不是象我們很多喜歡偷懶的人一樣,簡單的將整個DataTable一股腦的綁定到DataGrid,造成大量的數(shù)據(jù)冗余。 在這里,數(shù)據(jù)被真正的讀出來,并且被手動填充到一個自定義的對象數(shù)組中,我們來看看這個數(shù)組的定義: public class ProductResults
{private string m_productid;private string m_name;// product propspublic string productid {get { return m_productid; }set { m_productid = value; } }public string name {get { return m_name; }set { m_name = value; } }
}
非常之簡單,不過我有點奇怪為什么不使用struct呢?是不是.NET中struct和class的性能差距已經(jīng)可以忽略不計了? 分析總結(jié) 通過觀察這兩個商店的具體實現(xiàn),我們得到了兩個不同的數(shù)據(jù)訪問模式,Duwamish采用的是以DataSet為核心,因為DataSet提供了這方面大量的相關(guān)方法,所以整個應(yīng)用的數(shù)據(jù)傳輸,數(shù)據(jù)格式定義,數(shù)據(jù)校驗都圍繞著DataSet來進(jìn)行,整個架構(gòu)定義非常清晰和嚴(yán)謹(jǐn),但是卻顯得有些龐大。PetShop在整個程序中沒有采用一個DataSet,程序非常的簡潔,輕靈,但是沒有Duwamish那么強的健壯性。這兩個程序是Microsoft公司不同的小組寫出來的代碼,所以有著不同風(fēng)格。不過都應(yīng)該能代表.NET的標(biāo)準(zhǔn)模式。看到這里,你應(yīng)該對文章開頭提出的那些疑問有一個比較形象的認(rèn)識了吧。 另外,請再次注意,PetShop在打開數(shù)據(jù)連接之后,并沒有馬上讀取數(shù)據(jù),而是將DataReader傳遞給另外的對象來執(zhí)行數(shù)據(jù)讀的操作,然后才關(guān)閉連接。這樣,數(shù)據(jù)連接的時間加長了,而數(shù)據(jù)庫連接是一項非常寶貴的服務(wù)器資源,相比之下,Dawamish在連接數(shù)據(jù)庫之后馬上進(jìn)行填充,然后迅速釋放掉數(shù)據(jù)庫連接的方式更加有利于大量用戶的并發(fā)訪問。 再一點,上文的程序中沒有提到更新操作,PetShop采用的是使用Command對象執(zhí)行單個存儲過程的方式來進(jìn)行更新操作,是屬于一種在線即時數(shù)據(jù)更新模式。而Dawamish采用的是DataAdapter的Update方法,將DataSet的改變一次性的提交到數(shù)據(jù)庫中,屬于離線數(shù)據(jù)更新模式。這種模式的好處是可以一次性更新大批量數(shù)據(jù),減少數(shù)據(jù)庫的連接次數(shù)。缺點是如果數(shù)據(jù)庫在改動非常頻繁的情況下需要實時的跟蹤數(shù)據(jù)變化就不合適了。需要根據(jù)具體的情況采用具體的數(shù)據(jù)更新辦法。 總的來說,如果您只需要快速的讀取數(shù)據(jù)并顯示出來,推薦您采用DataReader,如果您需要對數(shù)據(jù)進(jìn)行大量的修改,還有大量并發(fā)訪問的可能,而且不需要實時的跟蹤數(shù)據(jù)庫的變化,推薦您使用DataSet。當(dāng)然,這兩種情況有點極端了,實際的應(yīng)用環(huán)境也許有著很復(fù)雜的條件,具體需要您自己審時度勢,綜合采用,不過我個人還是比較喜歡PetShop那種輕靈的風(fēng)格 :) 本文只嘗試對以上兩個典型的.NET應(yīng)用例程的數(shù)據(jù)訪問機制做了一個簡單的追蹤分析,如果有希望能對此例進(jìn)行其它方面的研究或者就本話題繼續(xù)進(jìn)行更加深入探討的朋友,請發(fā)Email到我的信箱:nluyan@163.NET,謝謝。
總結(jié)
以上是生活随笔為你收集整理的对比.Net PetShop和Duwamish来探讨Ado.Net的数据库编程模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于页面刷新的问题
- 下一篇: DataGrid入门经典(C#)