概述 ADO
NET為我們提供了強大的數據庫開發能力
它內置的多個對象為我們的數據庫編程提供了不同的選擇
但是在允許我們靈活選用的同時
許多初學者也很迷惑
我到底是應該使用DataReader還是應該使用DataAdapter?我只想讀取一小部分數據
難道我一定要Fill滿整個DataSet嗎?為什麼DataReader不能和RecordSet一樣提供一個數據更新的方法?DataSet到底有什麼好處?
在本文中
我將 PetShop的數據庫編程模式和Duwamish的數據庫編程模式進行一些簡單的分析和對比
如果您也有以上疑問的話
相信在讀完本文之後
就可以根據具體的需要來制定一個最適合您應用的數據庫編程模式
Net PetShop和Duwamish簡單介紹 相信大家一定聽說過有名的
寵物店大戰
沒錯
本文的主角之一就是獲勝方
Net PetShop
微軟號稱以
倍的速度和
/
的代碼量遙遙領先於基於J
EE的PetStore寵物商店
雖然SUN也曾對此抱怨過不滿
指責此
大戰
有水分
不過無論如何
Net PetShop絕對是一個經典的
Net實例教程
至少為我們提供了一條趕超J
EE的
捷徑=
)
它的下載地址是
Net PetShop寵物網上商店首頁
而Duwamish則是一個外表簡單內部卻極其復雜的一個網上書店的Net完整應用范例作為一個微軟官方的Sample它同時提供了C#和VBNet兩種語言版本並且還附上了大量詳盡的中文資料如果打印出來實在是居家旅行臨睡入廁必備之物什麼?您沒聽說過?呵呵如果您裝了Visual Studio Net的話它就在您的硬盤上靜靜的躺著呢不過還沒有被安裝您可以在您的 的EntERPrise Samples目錄下找到並安裝它例如C:\Program Files\Microsoft Visual Studio NET\Enterprise Samples\Duwamish CS
Duwamish網上電子書店首頁
結構簡述
兩家商店都采用了n層應用結構(毫無疑問n層結構的應用架構應該絕對是您開發Net應用的首選哪怕您只想做一個網頁計數器)不同的是PetShop采用的是最常見的三層應用結構分別為表示層中間層和數據層而Duwamish則采用的是一個四層應用結構並使用不同的項目分隔開分別為表示層業務外觀層業務規則層和數據層至於這兩種結構分別有什麼優點和缺點以及為什麼要這麼分層我們不進行詳細討論因為本文的重點不在於此我們主要分析的是他們的數據庫編程的模式
Duwamish數據訪問剖析
首先我們來看看Duwamish書店它采用的是DataAdapter和DataSet配合的數據存儲模式所不同的是它對DataSet進行子類化擴展作為數據載體也就是采用定制的DataSet來進行層間的數據傳輸下面是一個定制的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 = tableColumns;
columnsAdd(PKID_FIELD typeof(SystemInt));
columnsAdd(TYPE_ID_FIELD typeof(SystemInt));
columnsAdd(PUBLISHER_ID_FIELD typeof(SystemInt));
columnsAdd(PUBLICATION_YEAR_FIELD typeof(SystemInt));
columnsAdd(ISBN_FIELD typeof(SystemString));
columnsAdd(IMAGE_FILE_SPEC_FIELD typeof(SystemString));
columnsAdd(TITLE_FIELD typeof(SystemString));
columnsAdd(DESCRIPTION_FIELD typeof(SystemString));
columnsAdd(UNIT_PRICE_FIELD typeof(SystemDecimal));
columnsAdd(UNIT_COST_FIELD typeof(SystemDecimal));
columnsAdd(ITEM_TYPE_FIELD typeof(SystemString));
columnsAdd(PUBLISHER_NAME_FIELD typeof(SystemString));
thisTablesAdd(table);
}
………
}
我們可以看到它有一個BuildDataTables方法並且在構造函數中調用這樣定制的Books表就和這個DataSet捆綁在一起了省得以後還要進行Column Mapping這真是個好主意我怎麼就沒有想到呢? )
解決了數據結構接下來看看數據層的代碼實現在Duwamish中數據層中有個類分別是BooksCategoriesCustomers和Orders每個類分別只負責有關數據的存取下面是其中一個類的示例代碼
private SqlDataAdapter dsCommand;
public BookData GetBookById(int bookId)
{
return FillBookData(GetBookById @BookId bookIdToString());
}
private BookData FillBookData(String commandText String paramName String paramValue)
{
if (dsCommand == null )
{
throw new SystemObjectDisposedException( GetType()FullName );
}
BookData data = new BookData();
SqlCommand command = dsCommandSelectCommand;
commandCommandText = commandText;
commandCommandType = CommandTypeStoredProcedure; // use stored proc for perf
SqlParameter param = new SqlParameter(paramName SqlDbTypeNVarChar );
paramValue = paramValue;
commandParametersAdd(param);
dsCommandFill(data);
return data;
}
這裡就是數據層的代碼了我們在這裡可以看到Duwamish采用了DataAdapter來將數據填充到定制的DataSet中然後返回該DataSet我感到很奇怪的是在數據存取層中竟然可以看到GetBookById這樣具體的數據存取方法雖然最後還是有一個抽象出來的FillBookData方法但是上面還有三層啊底層都做到這份上了那上層都做些什麼呢?答案是數據檢查上層基本上都在做一些很嚴密的數據合法性校驗(當然也會包括一些比較復雜的事務邏輯但是並不多)示例代碼如下
public CustomerData GetCustomerByEmail(String emailAddress String password)
{
//
// Check preconditions
//
ApplicationAssertCheckCondition(emailAddress != StringEmpty Email address is required
ApplicationAssertLineNumber);
ApplicationAssertCheckCondition(password != StringEmpty Password is required
ApplicationAssertLineNumber);
//
// Get the customer dataSet
//
CustomerData dataSet;
using (DataAccessCustomers customersDataAccess = new DataAccessCustomers())
{
dataSet = customersDataAccessLoadCustomerByEmail(emailAddress);
}
//
// Verify the customers password
//
DataRowCollection rows = dataSetTables[CustomerDataCUSTOMERS_TABLE]Rows;
if ( ( rowsCount == ) && rows[][CustomerDataPASSWORD_FIELD]Equals(password) )
{
return dataSet;
}
else
{
return null;
}
}
在這個方法中真正進行數據存取的實際上只有
dataSet = customersDataAccessLoadCustomerByEmail(emailAddress);
這麼一句是直接調用的數據層其它都是在進行合法性校驗我們可以感悟到進行一個真正的企業級開發需要考慮的系統健壯性有多麼重要
PetShop數據訪問剖析
OKDuwamish看完了下面我們來看看PetShop的數據訪問機制
PetShop只有一個項目它采用的分層辦法是將中間層和數據層都寫成cs文件放在Components目錄裡其中數據層就是一個名為Database的類它封裝了所有對數據庫的底層操作下面是示例代碼段
public void RunProc(string procName out SqlDataReader dataReader) {
SqlCommand cmd = CreateCommand(procName null);
dataReader = cmdExecuteReader(SystemDataCommandBehaviorCloseConnection);
}
我們看到了一個跟Duwamish截然不同的另一種數據訪問方式它將所有的數據訪問方法抽象出來做成一個RunProc方法至於返回數據呢呵呵它有點偷懶直接返回一個DataReader給你你自己去讀吧還記得Duwamish采用的層間數據傳輸載體是什麼嗎?對了是DataSet它被數據層填充後返回給了中間層但是這裡數據層和傳輸層的數據傳輸載體變成了DataReader實際上還不能稱它為數據載體因為數據還沒開始讀呢在這裡DataReader的作用和指針有點類似也許我們應該稱它為數據引用)
接著往下看DataReader被怎麼處理的
public ProductResults[] GetList(string catid int currentPage int pageSize ref int numResults)
{
numResults = ;
int index=;
SqlDataReader reader = GetList(catid);
ProductResults[] results = new ProductResults[pageSize];
// now loop through the list and pull out items of the specified page
int start = (int)((currentPage ) * pageSize);
if (start <= ) start = ;
// skip
for (int i = ; i < start ; i++) {
if (readerRead()) numResults++;
}
if (start > ) readerRead();
// read the data we are interested in
while (readerRead()) {
if (index < pageSize) {
results[index] = new ProductResults();
results[index]productid = readerGetString();
results[index]name = readerGetString();
index++;
}
numResults++;
}
readerClose();
// see if need to redim array
if (index == pageSize)
return results;
else {
// not a full page redim array
ProductResults[] results = new ProductResults[index];
ArrayCopy(results results index);
return results;
}
}
注意到currentPage和pageSize了嗎?原來在這裡就進行了數據分頁只返回滿足需要的最少的數據量而不是象我們很多喜歡偷懶的人一樣簡單的將整個DataTable一股腦的綁定到DataGrid造成大量的數據冗余
在這裡數據被真正的讀出來並且被手動填充到一個自定義的對象數組中我們來看看這個數組的定義
public class ProductResults
{
private string m_productid;
private string m_name;
// product props
public 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的性能差距已經可以忽略不計了?
分析總結
通過觀察這兩個商店的具體實現我們得到了兩個不同的數據訪問模式Duwamish采用的是以DataSet為核心因為DataSet提供了這方面大量的相關方法所以整個應用的數據傳輸數據格式定義數據校驗都圍繞著DataSet來進行整個架構定義非常清晰和嚴謹但是卻顯得有些龐大PetShop在整個程序中沒有采用一個DataSet程序非常的簡潔輕靈但是沒有Duwamish那麼強的健壯性這兩個程序是Microsoft公司不同的小組寫出來的代碼所以有著不同風格不過都應該能代表Net的標准模式看到這裡你應該對文章開頭提出的那些疑問有一個比較形象的認識了吧
另外請再次注意PetShop在打開數據連接之後並沒有馬上讀取數據而是將DataReader傳遞給另外的對象來執行數據讀的操作然後才關閉連接這樣數據連接的時間加長了而數據庫連接是一項非常寶貴的服務器資源相比之下Dawamish在連接數據庫之後馬上進行填充然後迅速釋放掉數據庫連接的方式更加有利於大量用戶的並發訪問
再一點上文的程序中沒有提到更新操作PetShop采用的是使用Command對象執行單個存儲過程的方式來進行更新操作是屬於一種在線即時數據更新模式而Dawamish采用的是DataAdapter的Update方法將DataSet的改變一次性的提交到數據庫中屬於離線數據更新模式這種模式的好處是可以一次性更新大批量數據減少數據庫的連接次數缺點是如果數據庫在改動非常頻繁的情況下需要實時的跟蹤數據變化就不合適了需要根據具體的情況采用具體的數據更新辦法
總的來說如果您只需要快速的讀取數據並顯示出來推薦您采用DataReader如果您需要對數據進行大量的修改還有大量並發訪問的可能而且不需要實時的跟蹤數據庫的變化推薦您使用DataSet當然這兩種情況有點極端了實際的應用環境也許有著很復雜的條件具體需要您自己審時度勢綜合采用不過我個人還是比較喜歡PetShop那種輕靈的風格 )
本文只嘗試對以上兩個典型的Net應用例程的數據訪問機制做了一個簡單的追蹤分析
From:http://tw.wingwit.com/Article/program/net/201311/13509.html