.NET PetShop和Duwamish簡單介紹
相信大家一定聽說過有名的"寵物店大戰",沒錯,本文的主角之一就是獲勝方.NET PetShop,微軟號稱以27倍的速度和1/4的代碼量遙遙領先於基於J2EE的PetStore寵物商店。雖然SUN也曾對此抱怨過不滿,指責此"大戰"有水分,不過無論如何,.NET PetShop絕對是一個經典的.NET實例教程,至少為我們提供了一條趕超J2EE的“捷徑” :),它的下載地址是:http://www.gotdotnet.com/team/compare
.NET PetShop寵物網上商店首頁
而Duwamish則是一個外表簡單,內部卻極其復雜的一個網上書店的.NET完整應用范例,作為一個微軟官方的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網上電子書店首頁
結構簡述
兩家商店都采用了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 = 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方法,並且在構造函數中調用,這樣,定制的Books表就和這個DataSet捆綁在一起了,省得以後還要進行Column Mapping,這真是個好主意,我怎麼就沒有想到呢? :)
解決了數據結構,接下來看看數據層的代碼實現,在Duwamish中,數據層中有5個類,分別是Books,Categories,Customers和Orders,每個類分別只負責有關數據的存取。下面是其中一個類的示例代碼:
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 perf
SqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);
param.Value = paramValue;
command.Parameters.Add(param);
dsCommand.Fill(data);
return data;
}
這裡就是數據層的代碼了,我們在這裡可以看到Duwamish采用了DataAdapter來將數據填充到定制的DataSet中,然後返回該DataSet。我感到很奇怪的是在數據存取層中竟然可以看到GetBookById這樣具體的數據存取方法,雖然最後還是有一個抽象出來的FillBookData方法,但是上面還有三層啊,底層都做到這份上了,那上層都做些什麼呢?答案是數據檢查,上層基本上都在做一些很嚴密的數據合法性校驗(當然也會包括一些比較復雜的事務邏輯,但是並不多),示例代碼如下:
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