程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> ASP.NET基礎 >> 在ASP.NET 2.0中操作數據之六十五:在TableAdapters中創建新的存儲過程

在ASP.NET 2.0中操作數據之六十五:在TableAdapters中創建新的存儲過程

編輯:ASP.NET基礎

導言:

  本教程的Data Access Layer (DAL)使用的是類型化的數據集(Typed DataSets).就像我們在第一章《創建一個數據訪問層》裡探討的一樣,該類型化的數據集由強類型的DataTable和TableAdapter構成。DataTable描繪的是系統裡的邏輯實體而TableAdapter引用相關數據庫執行數據訪問,包括對DataTable填充數據、執行返回標量數據(scalar data)的請求、添加,更新,刪除數據庫裡的記錄等.

  TableAdapter執行的SQL命令要麼是某個特定的SQL statements,比如SELECT columnList FROM TableName;要麼是存儲過程.本教程前面部分的TableAdapter使用的是SQL statements.不過很多開發者和數據庫管理員基於安全、便於維護等方面的考慮,偏愛使用存儲過程;不過也有的人出於靈活性的考慮偏愛使用SQL statement.就我自己而言,我也偏向於存儲過程.在前面的文章,出於簡化的目的我選用的是SQL statements.

  當定義一個新TableAdapter或添加新方法時,使用TableAdapter的設置向導,我們可以很容易的創建新的或使用現有的存儲過程.在本文,我們將考察如何使用設置向導自動的生產存儲過程。在下一章我們考察如何設置TableAdapter的方法使用現有的或手動創建存儲過程.

  注意:關於討論到底使用存儲過程還是使用SQL statements的問題,可參考Rob Howard的博客文章《Don't Use Stored Procedures Yet?》(http://weblogs.asp.net/rhoward/archive/2003/11/17/38095.aspx)和Frans Bouma的博客文章《Stored Procedures are Bad, M'Kay?》(http://weblogs.asp.net/fboue/2003/11/18/38178.aspx)

存儲過程基礎

  一個存儲過程由一系列的T-SQL statement組成,當調用該存儲過程時就執行這些T-SQL statement.存儲過程可以接受0到多個輸入參數,返回標量值、輸出參數,或最常見的返回SELECT查詢值.

  注意:存儲過程Stored procedures也經常引用為“sprocs” or “SPs”.

  可以使用T-SQL statement語句CREATE PROCEDURE來創建存儲過程.比如下面的T-SQL腳本創建了一個名為GetProductsByCategoryID的存儲過程,它有一個名為 @CategoryID的參數,並且將表Products裡與CategoryID值相吻合的那條記錄的ProductID, ProductName, UnitPrice,以及Discontinued值返回.

CREATE PROCEDURE GetProductsByCategoryID
(
 @CategoryID int
)
AS

SELECT ProductID, ProductName, UnitPrice, Discontinued
FROM Products
WHERE CategoryID = @CategoryID

創建後,我們可以用下面的代碼調用它:

EXEC GetProductsByCategory categoryID

  注意:在下篇文章我們將在Visual Studio IDE集成環境裡創建存儲過程.不過在本文,我們將用TableAdapter向導來自動創建存儲過程.

  除了返回數據外,我們還可以在一個事務裡用存儲過程執行多條數據庫命令.比如,假如有一個名為DeleteCategory的存儲過程,其包含一個輸入參數@CategoryID,並執行2個DELETE statemets,第一個是刪除相關的products,第二個是刪除category。存儲過程裡面的多個statements並不是自動的封裝在一個事務裡的.我們應添加額外的T-SQL commands以確保存儲過程裡的多條數據庫命令當成原子操作處理.我們將在後面的內容考察如何用事務來封裝存儲過程的命令.

  當在體系的某個層使用存儲過程時,Data Access Layer的方法將調用某個具體的存儲過程而不是發出一個SQL statement命令.這樣一來我們可以發現、分析發出的查詢命令.並可以更清楚的看到數據庫是如何使用的.有關存儲過程基本原理的更多信息,可參考本文結束部分的延伸閱讀.

第一步:創建數據訪問層高級場景的Web頁面

在開始之前,讓我們花點時間創建本文及後面幾篇文章要用到的頁面。新建一個名為AdvancedDAL的文件夾,然後添加如下的ASP.NET頁面,記得使用母版頁Site.master:

Default.aspx
NewSprocs.aspx
ExistingSprocs.aspx
JOINs.aspx
AddingColumns.aspx
ComputedColumns.aspx
EncryptingConfigSections.aspx
ManagedFunctionsAndSprocs.aspx

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394888.png
圖1:添加相關的頁面

像其它文件夾一樣,Default.aspx頁面將列出本部分的內容,記得SectionLevelTutorialListing.ascx用戶控件提供了該功能。因此,將其從解決資源管理器裡拖放到Default.aspx頁面.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394811.png
圖2:將SectionLevelTutorialListing.ascx用戶控件拖到Default.aspx頁面

最後,將這些頁面添加到Web.sitemap文件裡。特別的,把下面的代碼放在“Working with Batched Data”

<siteMapNode>標簽後面:

<siteMapNode url="~/AdvancedDAL/Default.aspx"
 title="Advanced DAL Scenarios"
 description="Explore a number of advanced Data Access Layer scenarios.">
 
 <siteMapNode url="~/AdvancedDAL/NewSprocs.aspx"
 title="Creating New Stored Procedures for TableAdapters"
 description="Learn how to have the TableAdapter wizard automatically
 create and use stored procedures." />
 <siteMapNode url="~/AdvancedDAL/ExistingSprocs.aspx"
 title="Using Existing Stored Procedures for TableAdapters"
 description="See how to plug existing stored procedures into a
 TableAdapter." />
 <siteMapNode url="~/AdvancedDAL/JOINs.aspx"
 title="Returning Data Using JOINs"
 description="Learn how to augment your DataTables to work with data
 returned from multiple tables via a JOIN query." />
 <siteMapNode url="~/AdvancedDAL/AddingColumns.aspx"
 title="Adding DataColumns to a DataTable"
 description="Master adding new columns to an existing DataTable." />
 <siteMapNode url="~/AdvancedDAL/ComputedColumns.aspx"
 title="Working with Computed Columns"
 description="Explore how to work with computed columns when using
 Typed DataSets." />
 <siteMapNode url="~/AdvancedDAL/EncryptingConfigSections.aspx"
 title="Protected Connection Strings in Web.config"
 description="Protect your connection string information in
 Web.config using encryption." />
 <siteMapNode url="~/AdvancedDAL/ManagedFunctionsAndSprocs.aspx"
 title="Creating Managed SQL Functions and Stored Procedures"
 description="See how to create SQL functions and stored procedures
 using managed code." />
</siteMapNode>

更新Web.sitemap文件後,花點時間在浏覽器裡查看,左邊的菜單將包括本部分的內容.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394942.png
圖3:網站地圖現在包含了不部分的頁面

第二步:設置TableAdapter創建新的存儲過程

  我們在~/App_Code/DAL文件夾裡創建一個類型化的DataSet,名稱為NorthwindWithSprocs.xsd.由於我們在以前的教程裡已經詳細探討了創建細節,因此我們這裡一筆帶過,如果你想知道詳細的創建過程請參閱前面的第1章《創建一個數據訪問層》在DAL文件夾上右擊鼠標選“添加新項”,選DataSet模板,如圖4所示.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394990.png
圖4:新建一個名為NorthwindWithSprocs.xsd的數據集

  這樣將會創建一個新的類型化的DataSet,打開設計器,創建一個新的TableAdapter,展開TableAdapter設置向導.向導的第一步是讓我們選擇要連接的數據庫.在下拉列表裡有一個連接到Northwind數據庫的連接字符串,選中它,再點下一步。接下來的界面讓我們選擇TableAdapter以哪種方式訪問數據庫.在以前的教程裡我們選擇的是“Use SQL statements”,不過在本文我們選第二項:“Create new stored procedures”,點下一步.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394926.png
圖5:設置TableAdpater創建新的存儲過程

接下來,我們要指定主查詢(main query).我們將創建一個存儲過程來包含SELECT查詢.
使用下面的SELECT查詢:

SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916394931.png
圖6:鍵入SELECT查詢

  注意:在名為Northwind的數據集裡的ProductsTableAdapter的主查詢與上面本文定義的主查詢有所不同。那個主查詢還返回了每個產品的category名稱和company名稱.不過在後面的文章我們將對本文的TableAdapter添加這些相關的代碼.再點“Advanced Options”按鈕.我們可以指定是否讓向導為TableAdapter自動生成insert, update和delete statements;是否使用開發式並發操作(optimistic concurrency);是否完成inserts 和 update操作後刷新數據表.在默認情況下,自動選中“Generate Insert, Update and Delete statements”選項。另外,本文不用選擇“Use optimistic concurrency”項.當選擇自動創建存儲過程時,“Refresh the data table”項將被忽略掉.不管是否選中該項,最終的insert 和update存儲過程都會檢索剛添加或剛更新(just-inserted or just-updated record)的記錄,我們將在第三步看到.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395019.png
圖7:選中“Generate Insert, Update and Delete statements”項

  注意:當選中“Use optimistic concurrency”項的時候,向導會在WHERE語句裡添加額外的條件,當其它列的值發生改動的話,將阻止數據更新.關於使用TableAdapter內置的optimistic concurrency功能請參閱第21章《實現開放式並發》輸入SELECT主查詢並選取“Generate Insert, Update and Delete statements”項後,點下一步,接下來的界面,如圖8所示,讓我們為selecting, inserting, updating, 和deleting數據的存儲過程命名.將這些存儲過程的名字改為Products_Select, Products_Insert, Products_Update, 和Products_Delete.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395010.png
圖8:為存儲過程重命名

向導創建了4個存儲過程,點“Preview SQL Script”按鈕,你可以在Preview SQL Script 對話框裡將腳本保存在一個文件裡或復制到剪貼板.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395159.png
圖9:預覽生成的存儲過程

  對存儲過程重命名後,點下一步,對TableAdapter相應的方法命名.就像使用SQL statements一樣,我們可以創建方法來填充一個現有的DataTable或返回一個新的DataTable;我們也一個指定TableAdapter是否采用DB-Direct模式來插入、更新、刪除記錄.全選這3項,只不過將Return a DataTable方法重命名為GetProducts,如圖10所示:

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395147.png
圖10:將方法重命名為Fill 和GetProducts

點Next總覽向導將執行的步驟.點Finish按鈕完成設置.一旦向導結束後,將返回DataSet設計器,它此時將包括ProductsDataTable.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395123.png
圖11:DataSet設計器將顯示剛剛添加的ProductsDataTable

第三步:考察剛剛創建的存儲過程

  我們在第二步裡用向導創建了選擇、插入、更新、刪除數據的存儲過程.這些存儲過程可以通過Visual Studio查看或修改.打開服務器資源管理器,點到數據庫的存儲過程文件夾。如圖12所示,Northwind數據庫包含了4個新的存儲過程,Products_Delete, Products_Insert, Products_Select, and Products_Update.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395104.png
圖12:可以在Stored Procedures文件夾裡找到我們創建的4個存儲過程

  注意:如果你看不到服務器資源管理器,點“View”菜單,選Server Explorer項.如果你無法找到新創建的存儲過程,右擊Stored Procedures文件夾,選“刷新”.

  要查看或修改某個存儲過程,在服務器資源管理器裡雙擊其名字或右擊該存儲過程,選”打開“。如13顯示的是打開Products_Delete存儲過程的畫面.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395218.png
圖13:可以在Visual Studio裡打開並修改存儲過程

Products_Delete和Products_Select存儲過程的內容很好理解。比如下面的代碼構成了Products_Insert存儲過程.

ALTER PROCEDURE dbo.Products_Insert
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit
)
AS
 SET NOCOUNT OFF;
INSERT INTO [Products] ([ProductName], [SupplierID], [CategoryID], [QuantityPerUnit],
 [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued])
VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice,
 @UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued);
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
 UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = SCOPE_IDENTITY())

  在TableAdapter向導裡定義的SELECT查詢返回Products表裡的列,這些列又作為存儲過程的輸入參數並運用到INSERT statement中.緊接著的是一個SELECT查詢,返回Products表裡最新添加的記錄的各列的值(包括ProductID)。當使用Batch Update模式添加一個新記錄時,刷新功能是很有用的。因為它將最新添加的ProductRow instances實例的ProductID屬性賦值為數據庫指派的自增值.

  下面的代碼說明了該功能.代碼創建了基於NorthwindWithSprocs數據集的ProductsTableAdapter以及ProductsDataTable。要向數據庫添加一個新的產品,我們要創建一個ProductsRow instance實例,對其賦值,並調用TableAdapter的Update方法,再傳遞給ProductsDataTable.在內部,TableAdapter的Update方法遍歷傳遞給DataTable的所有ProductsRow instance實例(在本例,只有一個。因為我們只添加了一個產品),並執行相應的insert, update, 或delete命令。此時,執行Products_Insert存儲過程,其向Products表添加一條新記錄,並返回該記錄的詳細信息,然後更新ProductsRow instance實例的ProductID值。Update方法完成後,我們就可以通過ProductsRow的ProductID屬性訪問新添加記錄的ProductID值了.

// Create the ProductsTableAdapter and ProductsDataTable
NorthwindWithSprocsTableAdapters.ProductsTableAdapter productsAPI =
 new NorthwindWithSprocsTableAdapters.ProductsTableAdapter();
NorthwindWithSprocs.ProductsDataTable products =
 new NorthwindWithSprocs.ProductsDataTable();

// Create a new ProductsRow instance and set its properties
NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();
product.ProductName = "New Product";
product.CategoryID = 1; // Beverages
product.Discontinued = false;

// Add the ProductsRow instance to the DataTable
products.AddProductsRow(product);

// Update the DataTable using the Batch Update pattern
productsAPI.Update(products);

// At this point, we can determine the value of the newly-added record's ProductID
int newlyAddedProductIDValue = product.ProductID;

類似的,Products_Update存儲過程的UPDATE statement後面也包含一個SELECT statement,如下:

ALTER PROCEDURE dbo.Products_Update
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit,
 @Original_ProductID int,
 @ProductID int
)
AS
 SET NOCOUNT OFF;
UPDATE [Products]
SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
 [CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
 [UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
 [UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
 [Discontinued] = @Discontinued
WHERE (([ProductID] = @Original_ProductID));
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)

  我們注意到該存儲過程有2個關於ProductID的參數,即@Original_ProductID 和@ProductID,這樣以來我們就可以對主鍵值進行改動了.舉個例子:有一個employee(雇員)數據庫,每條employee記錄都用雇員的社保號碼作為其主鍵值.要想更改某條記錄的社保號碼,必須提供新的號碼以及原始號碼.不過對Products表來說用不著,因為列ProductID是一個唯一標識列(IDENTITY column),不應對其更改.實際上,Products_Update存儲過程裡的UPDATE statement並沒有包含ProductID列,因此,如果在UPDATE statement的WHERE字句裡使用@Original_ProductID的話,顯得多此一舉,而應該使用@ProductID參數.當更新某個存儲過程的參數時,TableAdapter裡所有那些調用該存儲過程方法都應該進行更新.

第四步:修改存儲過程的參數並更新TableAdapter

  由於@Original_ProductID參數是多余的,讓我們將其從Products_Update存儲過程裡完全清除.打開Products_Update存儲過程,刪除@Original_ProductID參數,在UPDATE statement的WHERE字句裡將@Original_ProductID改為@ProductID. 完成上述修改後,該存儲過程裡的T-SQL看起來應該和下面的差不多:

ALTER PROCEDURE dbo.Products_Update
(
 @ProductName nvarchar(40),
 @SupplierID int,
 @CategoryID int,
 @QuantityPerUnit nvarchar(20),
 @UnitPrice money,
 @UnitsInStock smallint,
 @UnitsOnOrder smallint,
 @ReorderLevel smallint,
 @Discontinued bit,
 @ProductID int
)
AS
 SET NOCOUNT OFF;
UPDATE [Products] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
 [CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
 [UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
 [UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
 [Discontinued] = @Discontinued
WHERE (([ProductID] = @ProductID));
 
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
 UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)

  按Ctrl+S或點工具欄裡的“保存”圖標,保存更改.此時,Products_Update存儲過程不會執行@Original_ProductID參數,但TableAdapter仍然會傳遞該參數.要想查看TableAdapter傳遞給Products_Update存儲過程的參數,你可以在設計器裡選中TableAdapter,轉到屬性窗口,點更新命令的參數集(UpdateCommand'sParameters collection)裡的橢圓型區域,這樣將轉到Parameters Collection Editor對話框,如圖14所示:

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395224.png
圖14:對話框裡列出了傳遞給Products_Update存儲過程的參數

要刪除參數,只需選中它,再點Remove按鈕.

  要刷新參數的話,你也可以在設計器裡選中TableAdapter,點右鍵選“設置”,這將會開啟TableAdapter設置向導,它列出了用於select, insert, updat和delete的存儲過程,並列出了這些存儲過程的輸入參數.如果你在Update下拉列表裡選Products_Update的話,你可以看到該存儲過程包含的輸入參數裡已經沒有包含@Original_ProductID了(見圖15),點Finish將對TableAdapter使用的參數集自動更新.

 https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395258.png
圖15:你可以通過使用TableAdapter的設置向導來刷新參數集

第五步:添加額外的TableAdapter方法

  我們在第二步說過,當創建一個新的TableAdapter時,很容易自動地生成相應的存儲過程,同樣我們也可以向TableAdapter添加額外的方法.作為演示,讓我們向ProductsTableAdapter添加一個方法GetProductByProductID(productID),該方法將一個ProductID作為輸入參數,並返回該產品的詳細信息.在ProductsTableAdapter上點擊右鍵,選擇“添加查詢”.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395246.png
圖16:向TableAdapter添加新查詢

  這將開啟TableAdapter查詢設置向導。首先,向導將詢問以何種方式訪問數據庫,我們將創建一個新的存儲過程,因此選“Create a new stored procedure”,再點Next.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395429.png
圖17:選中“Create a new stored procedure”項

  接下來,向導詢問我們執行哪種查詢,是返回一系列行?一個標量值?又或者執行UPDATE, INSERT,或 DELETE statement.由於GetProductByProductID(productID)方法將返回一行,我們選擇“SELECT which returns row”項,再點Next.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395420.png
圖18:選擇“SELECT which returns row” 項

  接下來的界面將展示TableAdapter的主查詢,其僅僅列出了存儲過程的名字(也就是dbo.Products_Select).將其刪除,替換為如下的SELECT statement,它返回某個具體產品的所有列.

SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395428.png
圖19:將存儲過程的名字替換為一個SELECT查詢.

  接下來要對創建的存儲過程命名,輸入Products_SelectByProductID,點Next.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395423.png
圖20:將新存儲過程命名為Products_SelectByProductID

  最後一步將要我們對自動生成的名字重新命名,並指定是否使用Fill a DataTable模式、是否使用Return a DataTable模式,抑或這2種模式都采用.就本文而言,都選中這2項並將方法重命名為FillByProductID 和 GetProductByProductID.點Next,再點Finish完成設置向導.

 https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395584.png

圖21:將TableAdapter的方法重命名為FillByProductID 和 GetProductByProductID

  完成向導後,TableAdapter將包含一個新的可用方法——GetProductByProductID(productID),當調用該方法時,將執行我們剛剛創建的Products_SelectByProductID存儲過程.花點時間在服務器資源管理器裡查看該存儲過程,點Stored Procedures文件夾,並打開Products_SelectByProductID(如果你沒看到它,在Stored Procedures文件夾上右擊鼠標,選“刷新”).

  請注意,SelectByProductID存儲過程將@ProductID作為輸入參數,並執行我們在向導裡輸入的SELECT Statement,如下:

ALTER PROCEDURE dbo.Products_SelectByProductID
(
 @ProductID int
)
AS
 SET NOCOUNT ON;

SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID

第六步:創建一個業務邏輯層類

在我們打算從表現層訪問產品前,我們首先需要為新添加的數據集創建一個BLL class,在~/App_Code/BLL文件夾裡創建一個ProductsBLLWithSprocs.cs文件,如下:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindWithSprocsTableAdapters;

[System.ComponentModel.DataObject]
public class ProductsBLLWithSprocs
{
 private ProductsTableAdapter _productsAdapter = null;
 protected ProductsTableAdapter Adapter
 {
 get
 {
  if (_productsAdapter == null)
  _productsAdapter = new ProductsTableAdapter();

  return _productsAdapter;
 }
 }

 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Select, true)]
 public NorthwindWithSprocs.ProductsDataTable GetProducts()
 {
 return Adapter.GetProducts();
 }


 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Select, false)]
 public NorthwindWithSprocs.ProductsDataTable GetProductByProductID(int productID)
 {
 return Adapter.GetProductByProductID(productID);
 }


 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Insert, true)]
 public bool AddProduct
 (string productName, int? supplierID, int? categoryID,
  string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
  short? unitsOnOrder, short? reorderLevel, bool discontinued)
 {
 // Create a new ProductRow instance
 NorthwindWithSprocs.ProductsDataTable products =
  new NorthwindWithSprocs.ProductsDataTable();
 NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();

 product.ProductName = productName;
 if (supplierID == null)
  product.SetSupplierIDNull();
 else
  product.SupplierID = supplierID.Value;
 if (categoryID == null)
  product.SetCategoryIDNull();
 else
  product.CategoryID = categoryID.Value;
 if (quantityPerUnit == null)
  product.SetQuantityPerUnitNull();
 else
  product.QuantityPerUnit = quantityPerUnit;
 if (unitPrice == null)
  product.SetUnitPriceNull();
 else
  product.UnitPrice = unitPrice.Value;
 if (unitsInStock == null)
  product.SetUnitsInStockNull();
 else
  product.UnitsInStock = unitsInStock.Value;
 if (unitsOnOrder == null)
  product.SetUnitsOnOrderNull();
 else
  product.UnitsOnOrder = unitsOnOrder.Value;
 if (reorderLevel == null)
  product.SetReorderLevelNull();
 else
  product.ReorderLevel = reorderLevel.Value;
 product.Discontinued = discontinued;

 // Add the new product
 products.AddProductsRow(product);
 int rowsAffected = Adapter.Update(products);

 // Return true if precisely one row was inserted, otherwise false
 return rowsAffected == 1;
 }

 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Update, true)]
 public bool UpdateProduct
 (string productName, int? supplierID, int? categoryID, string quantityPerUnit,
 decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
 short? reorderLevel, bool discontinued, int productID)
 {
 NorthwindWithSprocs.ProductsDataTable products =
  Adapter.GetProductByProductID(productID);
 if (products.Count == 0)
  // no matching record found, return false
  return false;

 NorthwindWithSprocs.ProductsRow product = products[0];

 product.ProductName = productName;
 if (supplierID == null)
  product.SetSupplierIDNull();
 else
  product.SupplierID = supplierID.Value;
 if (categoryID == null)
  product.SetCategoryIDNull();
 else
  product.CategoryID = categoryID.Value;
 if (quantityPerUnit == null)
  product.SetQuantityPerUnitNull();
 else
  product.QuantityPerUnit = quantityPerUnit;
 if (unitPrice == null)
  product.SetUnitPriceNull();
 else
  product.UnitPrice = unitPrice.Value;
 if (unitsInStock == null)
  product.SetUnitsInStockNull();
 else
  product.UnitsInStock = unitsInStock.Value;
 if (unitsOnOrder == null)
  product.SetUnitsOnOrderNull();
 else
  product.UnitsOnOrder = unitsOnOrder.Value;
 if (reorderLevel == null)
  product.SetReorderLevelNull();
 else
  product.ReorderLevel = reorderLevel.Value;
 product.Discontinued = discontinued;

 // Update the product record
 int rowsAffected = Adapter.Update(product);

 // Return true if precisely one row was updated, otherwise false
 return rowsAffected == 1;
 }

 [System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Delete, true)]
 public bool DeleteProduct(int productID)
 {
 int rowsAffected = Adapter.Delete(productID);

 // Return true if precisely one row was deleted, otherwise false
 return rowsAffected == 1;
 }
}

  該類和以前章節所創建的ProductsBLL class類差不多,只是它用的是數據集 NorthwindWithSprocs的ProductsTableAdapter 和 ProductsDataTable object對象。與ProductsBLL類使用using NorthwindTableAdapters不同,ProductsBLLWithSprocs類使用的是using NorthwindWithSprocsTableAdapters.同樣的,該類的ProductsDataTable和 ProductsRow對象使用的是NorthwindWithSprocs命名空間.我們的ProductsBLLWithSprocs class類提供了2種數據訪問方法GetProducts() 和GetProductByProductID().另外,還有添加、更新、刪除單個產品的方法.

第七步:在表現層出來數據集NorthwindWithSprocs

  此時,我們以及對數據訪問層和業務邏輯層做了相關改動,接下來我們要創建一個ASP.NET頁面調用BLL的ProductsBLLWithSprocs class類以展示、更新、刪除記錄.

  打開AdvancedDAL文件夾裡的NewSprocs.aspx頁面,從工具箱拖一個GridView控件到頁面,設置其ID為Products. 從GridView的智能標簽將其綁定到一個名為ProductsDataSource的ObjectDataSource,設置其調用ProductsBLLWithSprocs類.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395527.png
圖22:設置ObjectDataSource調用ProductsBLLWithSprocs類

  SELECT標簽的下拉列表裡有2個方法,GetProducts()和GetProductByProductID().由於我們將在GridView裡顯示所有的產品,所以我們選GetProducts()方法.在UPDATE, INSERT, 和DELETE標簽裡都只有一個方法,確保選中它們,點Finish按鈕。

  完成設置後,Visual Studio會向GridView添加BoundFields列以及一個CheckBoxField列, 啟用GridView控件的“編輯”和“刪除”功能.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395547.png
圖23:頁面包含一個可以分頁和排序的GridView控件.

  就像在以前的教程裡探討過的一樣,完成ObjectDataSource的設置後,Visual Studio 會自動的將OldValuesParameterFormatString屬性設置為“original_{0}”. 為使數據修改功能正常工作,要麼將該屬性刪除,要麼將其設置為“{0}”.

  在我們完成設置、啟用“編輯”和“刪除”功能、將OldValuesParameterFormatString屬性設為其默認值後,頁面的聲明代碼看起來應該和下面的差不多:

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
 DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
 <Columns>
 <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
 <asp:BoundField DataField="ProductID" HeaderText="ProductID"
  InsertVisible="False" ReadOnly="True"
  SortExpression="ProductID" />
 <asp:BoundField DataField="ProductName" HeaderText="ProductName"
  SortExpression="ProductName" />
 <asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
  SortExpression="SupplierID" />
 <asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
  SortExpression="CategoryID" />
 <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
  SortExpression="QuantityPerUnit" />
 <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
  SortExpression="UnitPrice" />
 <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
  SortExpression="UnitsInStock" />
 <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
  SortExpression="UnitsOnOrder" />
 <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
  SortExpression="ReorderLevel" />
 <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
  SortExpression="Discontinued" />
 </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
 DeleteMethod="DeleteProduct" InsertMethod="AddProduct"
 SelectMethod="GetProducts" TypeName="ProductsBLLWithSprocs"
 UpdateMethod="UpdateProduct">
 <DeleteParameters>
 <asp:Parameter Name="productID" Type="Int32" />
 </DeleteParameters>
 <UpdateParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="supplierID" Type="Int32" />
 <asp:Parameter Name="categoryID" Type="Int32" />
 <asp:Parameter Name="quantityPerUnit" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="unitsInStock" Type="Int16" />
 <asp:Parameter Name="unitsOnOrder" Type="Int16" />
 <asp:Parameter Name="reorderLevel" Type="Int16" />
 <asp:Parameter Name="discontinued" Type="Boolean" />
 <asp:Parameter Name="productID" Type="Int32" />
 </UpdateParameters>
 <InsertParameters>
 <asp:Parameter Name="productName" Type="String" />
 <asp:Parameter Name="supplierID" Type="Int32" />
 <asp:Parameter Name="categoryID" Type="Int32" />
 <asp:Parameter Name="quantityPerUnit" Type="String" />
 <asp:Parameter Name="unitPrice" Type="Decimal" />
 <asp:Parameter Name="unitsInStock" Type="Int16" />
 <asp:Parameter Name="unitsOnOrder" Type="Int16" />
 <asp:Parameter Name="reorderLevel" Type="Int16" />
 <asp:Parameter Name="discontinued" Type="Boolean" />
 </InsertParameters>
</asp:ObjectDataSource>

  此時,我們可以對GridView控件做些修改,比如在編輯界面裡使用確認控件,在CategoryID 和 SupplierID列放置DropDownList控件,當點擊Delete按鈕時彈出確認框等.由於在以前的教程我們探討過這些主題,我不打算在此多花筆墨。

  不管你做沒做這些改進,讓我們在浏覽器裡對頁面測試,如圖24所示.在GridView控件裡每行都可以編輯和刪除.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916395509.png
圖24:可以通過GridView對產品進行查看、編輯、刪除

結語:

  類型化數據集裡的TableAdapters可以通過ad-hoc SQL statement或存儲過程訪問數據庫裡的數據.當處理存儲過程時,我們要麼使用現有的存儲過程,要麼使用TableAdapter向導創建一個基於SELECT查詢的新的存儲過程.在本文,我們考察了如何自動的創建一個存儲過程.

  雖然自動創建可以節省時間,但是在某些情況下,向導自動創建的存儲過程與我們的期望值還是有差距.比如自動創建的Products_Update存儲過程,它包含@Original_ProductID 和 @ProductID這2個參數,但@Original_ProductID參數對我們來說是多余的.

在接下來的文章,我們將考察TableAdapter使用現有的存儲過程的情況.

  祝編程快樂!

作者簡介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。大家可以點擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數據教程》,希望對大家的學習ASP.NET有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved