新增其他 DataTable 資料行 (C#)
使用 TableAdapter 精靈建立具類型的 DataSet 時,相對應的 DataTable 會包含主資料庫查詢所傳回的資料行。 但有時候,DataTable 需要包含其他資料行。 在本教學課程中,我們將了解為什麼在需要額外 DataTable 資料行時建議使用預存程序。
簡介
將 TableAdapter 新增至具類型的 DataSet 時,相對應 DataTable 的結構描述是由 TableAdapter 的主查詢所決定。 例如,如果主查詢傳回資料欄位 A、B 和 C,DataTable 就會有三個名為 A、B 和 C 的相對應資料行。除了其主查詢之外,TableAdapter 還可以包含其他查詢,這可根據某些參數傳回資料子集。 例如,除了 ProductsTableAdapter
的主查詢傳回所有產品相關資訊之外,它也包含 GetProductsByCategoryID(categoryID)
和 GetProductByProductID(productID)
等方法,這些方法會根據提供的參數傳回特定產品資訊。
如果所有 TableAdapter 的方法傳回的資料欄位等於或少於主查詢中指定的資料欄位,則 DataTable 結構描述的模型可妥當地反映 TableAdapter 的主查詢。 如果 TableAdapter 方法需要傳回其他資料欄位,則我們應該相應展開 DataTable 的結構描述。 在使用主記錄項目符號清單搭配詳細資料 DataList 的主要/詳細資料報告教學課程中,我們新增了一個方法到 CategoriesTableAdapter
,這傳回了主要查詢中定義的 CategoryID
、CategoryName
和 Description
資料欄位,外加 NumberOfProducts
,這是報告與每個類別相關聯之產品數目的額外資料欄位。 我們手動將新的資料行新增至 CategoriesDataTable
,以便從這個新方法擷取 NumberOfProducts
資料欄位值。
如上傳檔案教學課程中所述,對於使用臨機操作 SQL 陳述式並且具有其資料欄位並不完全符合主查詢的的方法的 TableAdapter 時請務必謹慎。 如果重新執行 TableAdapter 組態精靈,它會更新所有 TableAdapter 方法,使其資料欄位清單符合主查詢。 因此,任何具有自訂資料行清單的方法都會還原為主查詢資料行清單,而不會傳回預期的資料。 使用預存程序時,不會發生此問題。
在本教學課程中,我們將探討如何擴充 DataTable 結構描述以包含其他資料行。 鑒於使用臨機操作 SQL 陳述式時 TableAdapter 的脆弱性,在本教學課程中,我們將使用預存程序。 如需有關設定 TableAdapter 以使用預存程序的詳細資訊,請參閱針對具類型的 DataSet 的 TableAdapter 建立新的預存程序教學課程。
步驟 1:將 PriceQuartile
資料行新增至 ProductsDataTable
在針對具類型的 DataSet 的 TableAdapter 建立新的預存程序教學課程中,我們建立了一個具類型的 DataSet,名為 NorthwindWithSprocs
。 此 DataSet 包含兩個 DataTable:ProductsDataTable
和 EmployeesDataTable
。 ProductsTableAdapter
具有下列三個方法:
GetProducts
- 主查詢,這會傳回Products
資料表的所有記錄GetProductsByCategoryID(categoryID)
- 傳回具有指定 categoryID 的所有產品。GetProductByProductID(productID)
- 傳回具有指定 productID 的特定產品。
主查詢和兩個額外的方法都會傳回相同的資料欄位,也就是 Products
資料表的所有資料行。 沒有相互關聯的子查詢或 JOIN
從 Categories
或 Suppliers
資料表提取相關資料。 因此,ProductsDataTable
對於 Products
資料表中每個欄位都有相對應的資料行。
對於本教學課程,讓我們將方法新增至 ProductsTableAdapter
,名為 GetProductsWithPriceQuartile
,這會傳回所有產品。 除了標準產品資料欄位之外,GetProductsWithPriceQuartile
也會包含一個 PriceQuartile
資料欄位,指出產品價格下跌的四分位數。 例如,價格在最貴的 25% 的產品,PriceQuartile
值為 1,而價格在最低的 25% 的產品,值為 4。 不過,在我們擔心建立預存程序來傳回這項資訊之前,我們必須先更新 ProductsDataTable
以包含資料行,在使用 GetProductsWithPriceQuartile
方法時保存 PriceQuartile
結果。
開啟 NorthwindWithSprocs
DataSet,並以滑鼠右鍵按一下 ProductsDataTable
。 從操作功能表中選擇 [新增],然後挑選 [資料行]。
圖 1:將新資料行新增至 ProductsDataTable
(按一下以檢視完整大小的影像)
這會將新資料行新增至名為 Column1 且類型為 System.String
的 DataTable。 我們需要將此資料行的名稱更新為 PriceQuartile,並將其類型更新為 System.Int32
,因為這將用來保存介於 1 到 4 之間的數字。 在 ProductsDataTable
中選取新增的資料行,然後從 [屬性] 視窗,將 Name
屬性設定為 PriceQuartile,並將 DataType
屬性設定為 System.Int32
。
圖 2:設定新資料行的 Name
和 DataType
屬性 (按一下以檢視完整大小的影像)
如 [圖 2] 所示,可以設定其他屬性,例如資料行中的值是否必須是唯一的、資料行是否為自動遞增的資料行、是否允許資料庫 NULL
值等等。 將這些值設定為預設值。
步驟 2:建立 GetProductsWithPriceQuartile
方法
ProductsDataTable
既已更新為包含 PriceQuartile
資料行,我們現在準備好建立 GetProductsWithPriceQuartile
方法。 首先,以滑鼠右鍵按一下 TableAdapter,然後從操作功能表選擇 [新增查詢]。 這會顯示 TableAdapter 查詢組態精靈,它會先提示我們是否要使用臨機操作 SQL 陳述式或新的或現有的預存程序。 由於我們還沒有傳回價格四分位數資料的預存程序,因此讓我們允許 TableAdapter 為我們建立此預存程序。 選取 [建立新預存程序] 選項,然後按 [下一步]。
圖 3:指示 TableAdapter 精靈為我們建立預存程序 (按一下以檢視完整大小的影像)
在後續畫面中,如 [圖 4] 所示,精靈會詢問我們要新增的查詢類型。 由於 GetProductsWithPriceQuartile
方法會傳回 Products
資料表中的所有資料行和記錄,請選取 SELECT,這會傳回資料列選項,然後按 [下一步]。
圖 4:我們的查詢將是傳回多個資料列的 SELECT
陳述式 (按一下以檢視完整大小的影像)
接下來,系統會提示您提供 SELECT
查詢。 在精靈中輸入下列查詢:
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
NTILE(4) OVER (ORDER BY UnitPrice DESC) as PriceQuartile
FROM Products
上述查詢使用 SQL Server 2005 的新 NTILE
函式將結果分成四個群組,其中群組是由以遞減順序排序的 UnitPrice
值所決定。
不幸的是,查詢產生器不知道如何剖析 OVER
關鍵字,而且會在剖析上述查詢時顯示錯誤。 因此,在精靈的文字方塊中直接輸入上述查詢,而不要使用查詢產生器。
注意
如需有關 NTILE 和 SQL Server 2005 其他排名函式的詳細資訊,請參閱 SQL Server 2005 線上叢書中的 ROW_NUMBER (Transact-SQL) 和排名函式一節。
輸入 SELECT
查詢並按一下 [下一步] 之後,精靈會要求我們為它將建立的預存程序提供名稱。 將新的預存程序命名為 Products_SelectWithPriceQuartile
,然後按一下 [下一步]。
圖 5:命名預存程序 Products_SelectWithPriceQuartile
(按一下以檢視完整大小的影像)
最後,系統會提示我們命名 TableAdapter 方法。 將 [填滿 DataTable] 和 [傳回 DataTable] 核取方塊保留為核取狀態,並命名方法 FillWithPriceQuartile
和 GetProductsWithPriceQuartile
。
圖 6:為 TableAdapter 的方法命名 ,然後按一下 [完成] (按一下以檢視完整大小的影像)
指定了 SELECT
查詢並且為預存程序和 TableAdapter 方法命名之後,按一下 [完成] 以完成精靈。 此時,您可能會從精靈收到一個或兩個警告,指出不支援 OVER
SQL 建構函式或陳述式。 可以忽略這些警告。
完成精靈之後,TableAdapter 應該包含 FillWithPriceQuartile
和 GetProductsWithPriceQuartile
方法,而且資料庫應該包含名為 Products_SelectWithPriceQuartile
的預存程序。 請花點時間確認 TableAdapter 確實包含這個新方法,而且預存程序已正確新增至資料庫。 檢查資料庫時,如果您沒有看到預存程序,請嘗試以滑鼠右鍵按一下 [預存程序] 資料夾,然後選擇 [重新整理]。
圖 7:確認已將新方法新增至 TableAdapter
圖 8:確定資料庫包含 Products_SelectWithPriceQuartile
預存程序 (按一下以檢視完整大小的影像)
注意
使用預存程序而非臨機操作 SQL 陳述式的優點之一,就是重新執行 TableAdapter 組態精靈不會修改預存程序資料行清單。 在 TableAdapter 上按一下滑鼠右鍵,從操作選單選擇 [設定] 選項以啟動精靈,然後按下 [完成] 完成精靈以進行確認。 接下來,移至資料庫並檢視 Products_SelectWithPriceQuartile
預存程序。 請注意,其資料行清單尚未修改。 如果我們使用臨機操作 SQL 陳述式,重新執行 TableAdapter 組態精靈會還原此查詢資料行清單以符合主查詢資料行清單,因而從 GetProductsWithPriceQuartile
方法所使用的查詢中移除 NTILE 陳述式。
叫用資料存取層的 GetProductsWithPriceQuartile
方法時,TableAdapter 會執行 Products_SelectWithPriceQuartile
預存程序,並針對每個傳回的記錄新增一個資料列至 ProductsDataTable
。 預存程序傳回的資料欄位會對應到 ProductsDataTable
資料行。 由於預存程序有傳回 PriceQuartile
資料欄位,因此會將其值指派給 ProductsDataTable
的 PriceQuartile
資料行。
對於其查詢未傳回 PriceQuartile
資料欄位的那些 TableAdapter 方法,PriceQuartile
資料行的值是其 DefaultValue
屬性所指定的值。 如 [圖 2] 所示,此值會設定為 DBNull
,即預設值。 如果您想要不同的預設值,只要據此設定 DefaultValue
屬性即可。 只要確定對於資料行的 DataType
(亦即 PriceQuartile
資料行的 System.Int32
),DefaultValue
值是有效的。
此時,我們已執行將其他資料行新增至 DataTable 的必要步驟。 若要確認此額外的資料行如預期般運作,讓我們建立一個 ASP.NET 頁面,以顯示每個產品名稱、價格和價格四分位數。 不過,在我們這麼做之前,必須先更新商務邏輯層,以包含一個方法,來呼叫 DAL 的 GetProductsWithPriceQuartile
方法。 接下來,我們將在步驟 3 中更新 BLL,然後在步驟 4 中建立 ASP.NET 頁面。
步驟 3:擴增商務邏輯層
在我們從展示層使用新的 GetProductsWithPriceQuartile
方法之前,必須先將相對應的方法新增至 BLL。 開啟 ProductsBLLWithSprocs
類別檔案並加入下列程式碼:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public NorthwindWithSprocs.ProductsDataTable GetProductsWithPriceQuartile()
{
return Adapter.GetProductsWithPriceQuartile();
}
如同 ProductsBLLWithSprocs
中的其他資料擷取方法,GetProductsWithPriceQuartile
方法會直接呼叫 DAL 相對應的 GetProductsWithPriceQuartile
方法,並傳回其結果。
步驟 4:在 ASP.NET Web Pages 中顯示價格四分位數資訊
隨著 BLL 新增完成,我們就可以建立 ASP.NET 頁面,以顯示每個產品的價格四分位數。 開啟 AdvancedDAL
資料夾中的 AddingColumns.aspx
頁面,並將 GridView 從 [工具箱] 拖曳至設計工具,將其 ID
屬性設定為 Products
。 從 GridView 的智慧標記,將它繫結至名為 ProductsDataSource
的新 ObjectDataSource。 將 ObjectDataSource 設定為使用 ProductsBLLWithSprocs
類別的 GetProductsWithPriceQuartile
方法。 由於這將是唯讀的格線,因此請將 UPDATE、INSERT 和 DELETE 索引標籤中的下拉式清單設定為 [無]。
圖 9:將 ObjectDataSource 設定為使用 ProductsBLLWithSprocs
類別 (按一下以檢視完整大小的影像)
圖 10:從 GetProductsWithPriceQuartile
方法擷取產品資訊 (按一下以檢視完整大小的影像)
完成 [設定資料來源精靈] 之後,Visual Studio 會自動針對方法傳回的每個資料欄位,新增 BoundField 或 CheckBoxField 到 GridView。 其中一個資料欄位是 PriceQuartile
,也就是我們在步驟 1 中新增至 ProductsDataTable
的資料行。
編輯 GridView 的欄位,移除 ProductName
、UnitPrice
和 PriceQuartile
BoundFields 之外的所有欄位。 設定 UnitPrice
BoundField 以將其值格式化為貨幣,並分別將 UnitPrice
和 PriceQuartile
BoundFields 靠右對齊和置中對齊。 最後,將其餘 BoundFields HeaderText
屬性分別更新為 Product、Price 和 Price Quartile。 此外,請核取 GridView 智慧標記中的 [啟用排序] 核取方塊。
完成這些修改之後,GridView 和 ObjectDataSource 的宣告式標記看起來應該如下所示:
<asp:GridView ID="Products" runat="server" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice">
<ItemStyle HorizontalAlign="Right" />
</asp:BoundField>
<asp:BoundField DataField="PriceQuartile" HeaderText="Price Quartile"
SortExpression="PriceQuartile">
<ItemStyle HorizontalAlign="Center" />
</asp:BoundField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductsWithPriceQuartile"
TypeName="ProductsBLLWithSprocs">
</asp:ObjectDataSource>
[圖 11] 顯示此頁面在透過瀏覽器瀏覽時樣子。 請注意,一開始,產品會依其價格依遞減順序排序,每個產品會指派一個適當的 PriceQuartile
值。 當然,此資料可以依據其他準則來排序,而 [價格四分位數] 資料行值仍舊反映產品相對於價格的排名 (見 [圖 12])。
圖 11:產品依價格排序 (按一下以檢視全尺寸影像)
圖 12:產品依名稱排序 (按一下以檢視全尺寸影像)
注意
只要幾行程式碼,我們就可以擴增 GridView,使其根據 PriceQuartile
值為產品資料列著色。 我們可以將第一個四分位數中的產品著色為淺綠色、將第二個四分位數著色為淺黃色等。 我鼓勵您花點時間新增這項功能。 如果您需要複習一下如何格式化 GridView,請參閱根據資料自訂格式化教學課程。
替代方法 - 建立另一個 TableAdapter
如本教學課程中所見,在 TableAdapter 新增方法以傳回主查詢所輸出的資料欄位以外的資料欄位時,我們可以將相對應的資料行新增至 DataTable。 然而,只有在 TableAdapter 中有少數方法傳回不同資料欄位,以及那些替代資料欄位與主查詢沒有太大差異時,這種方法才有效。
您可以改將另一個 TableAdapter 新增至 DataSet (包含來自第一個 TableAdapter 可傳回不同資料欄位的方法),而不是將資料行新增至 DataTable。 在本教學課程中,我們可以將額外的 TableAdapter 新增至名為 ProductsWithPriceQuartileTableAdapter
的 DataSet (使用 Products_SelectWithPriceQuartile
預存程序做為其主查詢),而不是將 PriceQuartile
資料行新增至 ProductsDataTable
(只能由 GetProductsWithPriceQuartile
方法使用)。 需要取得價格四分位數之產品資訊的 ASP.NET 頁面會使用 ProductsWithPriceQuartileTableAdapter
,而那些不需要的頁面可以繼續使用 ProductsTableAdapter
。
藉由加入新的 TableAdapter,DataTables 不會受影響,且其資料行會準確地鏡像其 TableAdapter 的方法所傳回的資料欄位。 不過,其他 TableAdapter 可能引入重複的工作和功能。 例如,如果顯示 PriceQuartile
資料行的 ASP.NET 頁面也需要提供插入、更新和刪除支援,則必須正確設定其 ProductsWithPriceQuartileTableAdapter
、InsertCommand
、UpdateCommand
和 DeleteCommand
屬性。 雖然這些屬性會鏡像 ProductsTableAdapter
,但此組態會產生額外的步驟。 此外,現在有兩種方式可更新、刪除或新增產品至資料庫 - 透過 ProductsTableAdapter
和 ProductsWithPriceQuartileTableAdapter
類別。
本教學課程的下載檔案包含 NorthwindWithSprocs
DataSet 中的 ProductsWithPriceQuartileTableAdapter
類別,當中說明了這個替代方法。
摘要
在大部分情況下,TableAdapter 中的所有方法都會傳回同一組資料欄位,但有時候特定的一兩種方法可能需要傳回其他欄位。 例如,在使用主記錄項目符號清單搭配詳細資料 DataList 的主要/詳細資料報告教學課程中,我們新增了一個方法到 CategoriesTableAdapter
,這除了主查詢的資料欄位外,還會傳回 NumberOfProducts
欄位,報告與每個類別相關聯的產品數目。 在本教學課程中,我們探討如何在 ProductsTableAdapter
中新增方法,除了主查詢的資料欄位之外,也傳回了 PriceQuartile
欄位。 若要擷取 TableAdapter 方法傳回的其他資料欄位,我們需要將相對應的資料行新增至 DataTable。
如果您打算手動將資料行新增至 DataTable,建議 TableAdapter 使用預存程序。 如果 TableAdapter 使用臨機操作 SQL 陳述式,則每當執行 TableAdapter 組態精靈時,所有方法資料欄位清單都會還原為主查詢所傳回的資料欄位。 此問題不會延伸至預存程序,這正是建議使用預存程序以及在本教學課程中使用它們的原因。
祝您程式設計愉快!
關於作者
Scott Mitchell,七本 ASP/ASP.NET 書籍的作者和 4GuysFromRolla.com 創始人,自 1998 年以來便開始使用 Microsoft Web 技術。 Scott 擔任獨立顧問、講師和作家。 他的新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 您可以透過 mitchell@4GuysFromRolla.com 或他的部落格 (可以在 http://ScottOnWriting.NET 找到) 與他聯繫。
特別感謝
本教學課程系列已經過許多熱心的檢閱者檢閱。 本教學課程的主要檢閱者是 Randy Schmidt、Jacky Goor、Bernadette Leigh 和 Hilton Giesenow。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果有,請發信到 mitchell@4GuysFromRolla.com 。