從存儲過程返回值
以上示例有一個不足之處。northwind customers 表使用數字字母形式的主鍵,并且必須由插入數據的應用程序生成。也就是說,如果使用以上程序插入新記錄,則必須為 customerid 自行創建由五個字符組成的值。
在真實軟件中,為新記錄自動生成主鍵更為常見。主鍵通常是按順序分配的長整數。
為新記錄設置主鍵有兩種基本技術。應用程序可調用生成下一個可用 id 的存儲過程,然后將此 id 直接放到 dataset 的新行中。或者,用于插入記錄的存儲過程可以為記錄派生新 id,然后將其作為返回值傳遞回應用程序。
第一種技術需要一點額外的邏輯來獲取新 id 并將其放到新記錄的相應位置。使用存儲過程執行插入操作與以上示例類似。
但第二種技術要求在存儲過程中使用一種新型參數。到目前為止我們見到的所有參數都是默認類型,即輸入參數。實際上參數分四種類型:
input 此參數只用于將信息從應用程序傳輸到存儲過程。
inputoutput 此參數可將信息從應用程序傳輸到存儲過程,并將信息從存儲過程傳輸回應用程序。
output 此參數只用于將信息從存儲過程傳輸回應用程序。
returnvalue 此參數表示存儲過程的返回值。sql server 的存儲過程參數列表中不顯示該參數。它只與存儲過程的 return 語句中的值相關聯。
存儲過程為主鍵生成新值后,通常使用存儲過程中的 return 語句返回該值,因此用來訪問該值的參數類型是 returnvalue 參數。
returnvalue 參數與其他類型的參數有一個重要的區別。通常,在 ado.net 中為 command 對象配置的參數的順序并不重要。參數名稱只用來與存儲過程中相應的參數相匹配。但是,對于 returnvalue 參數,它必須是列表中的第一個參數。
也就是說,為 command 對象配置 returnvalue 參數時,必須首先在代碼中配置該參數,這樣它才能獲取集合中的第一個數字索引。如果先配置任何其他參數,returnvalue 參數將不能正常工作。
為了說明帶返回值的存儲過程的用法,我們編寫一個在 northwind products 表中插入記錄的示例。此表被設置為使用 identity 列自動創建新產品 id。遺憾的是,northwind 示例數據庫不包含執行所需操作的存儲過程,所以在完成示例其余部分之前,我們需要向數據庫插入一個這樣的存儲過程。
轉到 visual studio .net 中的 server explorer(服務器資源管理器)。打開 sql server 的節點,打開 sql server 實例的節點,然后打開 northwind 數據庫的節點。
右鍵單擊 stored procedures(存儲過程)節點,選擇 new stored procedure(新建存儲過程)。在出現的編輯窗口中,用以下文本替換其中的所有文本:
alter procedure dbo.msdninsertproduct
(
@productname nvarchar(40),
@supplierid int,
@categoryid int,
@quantityperunit nvarchar(20),
@unitprice money,
@unitsinstock smallint,
@unitsonorder smallint,
@reorderlevel smallint,
@discontinued bit
)
as
declare @productid int
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 = @@identity
return @productid
現在關閉編輯窗口,當系統詢問您是否要保存更改時,單擊 yes(是)。現在存儲過程就已保存到數據庫中,并被命名為 msdninsertproduct。
現在便可以編寫代碼來使用此存儲過程。新建 windows 應用程序,在空白 form1 上,放置錨定到所有四個邊的 datagrid,還需添加名為 btnfill 和 btninsertproduct 的兩個按鈕。將 btnfill 的 text 屬性設置為 fill,將 btninsertproduct 的 text 屬性設置為 insert product。
在 btnfill 的 click 事件中,放置以下代碼:
dim sconnectionstring as string = _
"server=localhost;uid=sa;pwd=;database=northwind"
dim ssql as string = "select * from products"
dim dagetproducts as new sqldataadapter(ssql, sconnectionstring)
dim dsproducts as new dataset()
dagetproducts.fill(dsproducts, "products")
datagrid1.datasource = dsproducts
它與本文前面所講的代碼大致相同,所以我們不再贅述。不要忘記必要時更改連接字符串,并在項目代碼的頂部為 sqlclient 命名空間放置 imports 語句。然后在 btninsertproduct 的 click 事件中放置以下代碼:
dim sconnectionstring as string = _
"server=localhost;uid=sa;pwd=;database=northwind"
dim cnnorthwind as new sqlconnection(sconnectionstring)
dim cmdinsertproduct as new sqlcommand("msdninsertproduct", cnnorthwind)
cmdinsertproduct.commandtype = commandtype.storedprocedure
' 為存儲過程設置參數
cmdinsertproduct.parameters.add(new sqlparameter("@return_value", sqldbtype.int, 4, "productid"))
cmdinsertproduct.parameters("@return_value").direction = parameterdirection.returnvalue
cmdinsertproduct.parameters.add(new sqlparameter("@productname", _
sqldbtype.nvarchar, 40, "productname"))
cmdinsertproduct.parameters.add(new sqlparameter("@supplierid", _
sqldbtype.int, 4, "supplierid"))
cmdinsertproduct.parameters.add(new sqlparameter("@categoryid", _
sqldbtype.int, 4, "categoryid"))
cmdinsertproduct.parameters.add(new sqlparameter("@quantityperunit", _
sqldbtype.nvarchar, 20, "quantityperunit"))
cmdinsertproduct.parameters.add(new sqlparameter("@unitprice", _
sqldbtype.money, 8, "unitprice"))
cmdinsertproduct.parameters.add(new sqlparameter("@unitsinstock", _
sqldbtype.smallint, 2, "unitsinstock"))
cmdinsertproduct.parameters.add(new sqlparameter("@unitsonorder", _
sqldbtype.smallint, 2, "unitsonorder"))
cmdinsertproduct.parameters.add(new sqlparameter("@reorderlevel", _
sqldbtype.smallint, 2, "reorderlevel"))
cmdinsertproduct.parameters.add(new sqlparameter("@discontinued", _
sqldbtype.bit, 1, "discontinued"))
dim dainsertproduct as new sqldataadapter()
dainsertproduct.insertcommand = cmdinsertproduct
dim dsproducts as dataset = ctype(datagrid1.datasource, dataset)
dim drnewproduct as datarow
drnewproduct = dsproducts.tables("products").newrow
drnewproduct.item("productname") = "billy's sesame oil"
drnewproduct.item("supplierid") = 4
drnewproduct.item("categoryid") = 7
drnewproduct.item("quantityperunit") = "6 10oz bottles"
drnewproduct.item("unitprice") = 69
drnewproduct.item("unitsinstock") = 12
drnewproduct.item("unitsonorder") = 0
drnewproduct.item("reorderlevel") = 6
drnewproduct.item("discontinued") = false
dsproducts.tables("products").rows.add(drnewproduct)
dainsertproduct.update(dsproducts.tables("products"))
msgbox(drnewproduct.item("productid"))
此代碼與如上所示的代碼類似,只是為返回值配置參數的代碼行不同。請注意,它是第一個參數,并被設置為將返回值放回到 productid 字段中。
用于向數據集中插入新行的代碼是標準 ado.net 代碼,所以我們就不再贅述。它為產品記錄創建一行新的適當結構(使用產品 datatable 的 newrow 方法),然后將數據放入行中,最后向產品 datatable 的 rows 集合中添加行。
現在運行程序進行測試。單擊 fill 按鈕,但不對網格中的數據進行任何更改。然后按 insert product 按鈕。將插入 billy's sesame oil 的新產品記錄,并且出現的消息框會通知您為其返回的 productid。還可以打開網格中的 products 表,滾動到底部,并看到已添加了新行。
使用 server explorer(服務器資源管理器)編寫參數代碼
以上代碼編寫起來既冗長又繁瑣。但是,dataadapter configuration wizard(數據適配器配置向導)提示可以使用 visual studio 為我們編寫此代碼。dataadapter configuration wizard(數據適配器配置向導)為完整配置所需的四個存儲過程(分別是 select、update、insert 和 delete)生成了代碼。假設您象以上示例一樣只需要一個存儲過程的代碼,可以將其截短。要獲得只與一個存儲過程通信的預先編寫好的代碼,只需展開 server explorer(服務器資源管理器)以顯示需要訪問的存儲過程,然后將該存儲過程拖到設計界面上。將看到為該存儲過程創建的 dataadapter 和 command 對象,代碼的設計器部分包含為該存儲過程配置參數所需的所有代碼。可以按原樣使用該代碼,也可以根據需要復制并調整后使用。
小結
本文中的示例仍是演示軟件,但至少足以向您說明如何訪問存儲過程,以便您開始編寫自己的真實軟件。當然,您需要了解要訪問的存儲過程,并且可能需要向數據庫管理員 (dba) 或其他組員咨詢以獲取該信息。
對于復雜系統,存儲過程有許多優勢。希望您在本文中學到了足夠的知識,可以不必擔心如何開始使用它們。第一次嘗試編寫代碼時,您可能希望使用 dataadapter wizard(dataadapter 向導)或 server explorer(服務器資源管理器)。但如果您能在必要時自行編寫訪問代碼,則可以更有效地使用存儲過程。,歡迎訪問網頁設計愛好者web開發。