使用 SqlDataSource 插入、更新和刪除資料 (C#)
在前面的教學課程中,我們了解了 ObjectDataSource 控制項如何允許插入、更新和刪除資料。 SqlDataSource 控制項支援相同的操作,但方法不同,本教學課程示範如何設定 SqlDataSource 來插入、更新和刪除資料。
簡介
如同在插入、更新和刪除概述中所討論的,GridView 控制項提供內建的更新和刪除功能,而DetailsView 和FormView 控制項則包括插入支援以及編輯和刪除功能。 這些資料修改功能可以直接插入到資料來源控制項中,無需編寫一行程式碼。 插入、更新和刪除概述檢查如何使用 ObjectDataSource 方便使用 GridView、DetailsView 和 FormView 控制項進行插入、更新和刪除。 或者,可以使用 SqlDataSource 取代 ObjectDataSource。
回想一下,為了支援插入、更新和刪除,我們需要使用 ObjectDataSource 指定要呼叫的物件層方法來執行插入、更新或刪除操作。 對於SqlDataSource,我們需要提供 INSERT
、UPDATE
、DELETE
和 SQL 陳述式 (或預存程序) 來執行。 正如我們將在本教學課程中看到的,這些陳述式可以手動建立,也可以由 SqlDataSource 配置資料來源精靈自動產生。
注意
由於我們已經討論了 GridView、DetailsView 和 FormView 控制項的插入、編輯和刪除功能,因此本教學課程將重點放在如何設定 SqlDataSource 控制項以支援這些操作。 如果您需要溫習如何在 GridView、DetailsView 和 FormView 中實現這些功能,請返回編輯、插入和刪除資料教學課程,從插入、更新和刪除概述開始。
步驟 1:指定 INSERT、UPDATE 和 DELETE 陳述式
正如我們在過去的兩個教學課程中所看到的,要從 SqlDataSource 控制項檢索資料,我們需要設定兩個屬性:
ConnectionString
,它指定將查詢發送到哪個資料庫,以及SelectCommand
,它指定要執行以傳回結果的臨機操作 SQL 陳述式或預存程序名稱。
對於帶有參數的 SelectCommand
值,參數值透過 SqlDataSource SelectParameters
集合指定,可以包含硬編碼值、公用參數來源值 (查詢字串欄位、會話變數、Web 控制項值等),也可以透過程式設計方式指派。 當以程式設計方式或從資料 Web 控制項自動呼叫 SqlDataSource 控制項的 Select()
方法時,會建立與資料庫的連接,將參數值指派給查詢,並將指令傳送到資料庫。 然後,結果將作為 DataSet 或 DataReader 傳回,這取決於控制項 DataSourceMode
屬性的值。
除了選擇資料之外,SqlDataSource 控制項還可以透過以大致相同的方式提供 INSERT
、UPDATE
和 DELETE
SQL 陳述式來插入、更新和刪除資料。 只需將 InsertCommand
、UpdateCommand
和 DeleteCommand
屬性指派給要執行的 INSERT
、UPDATE
和 DELETE
SQL 陳述式即可。 如果陳述式具有參數 (大多數情況下都會如此),請將它們包含在 InsertParameters
、UpdateParameters
和 DeleteParameters
集合中。
指定 InsertCommand
、UpdateCommand
或 DeleteCommand
值後,對應資料 Web 控制項的智慧標記中的啟用插入、啟用編輯或啟用刪除選項將變為可用。 為了說明這一點,我們以我們在使用 SqlDataSource 控制項查詢資料教學課程中建立的 Querying.aspx
頁面為例,並對其進行擴充以包含刪除功能。
首先開啟 SqlDataSource
資料夾中的 InsertUpdateDelete.aspx
和 Querying.aspx
頁面。 從 Querying.aspx
頁面上的設計器中,選擇第一個範例中的 SqlDataSource 和 GridView (ProductsDataSource
和 GridView1
控制項)。 選擇兩個控制項後,請前往「編輯」功能表並選擇「複製」(或直接按 Ctrl+C)。 接下來,轉到 InsertUpdateDelete.aspx
設計器並貼上控制項。 將兩個控制項移至 InsertUpdateDelete.aspx
後,在瀏覽器中測試該頁面。 您應該會看到 Products
資料庫表中所有記錄的 ProductID
、ProductName
和 UnitPrice
列的值。
圖 1:所有產品均已列出,排序依據 ProductID
(點擊可看大圖)
新增 SqlDataSource 的 DeleteCommand 和 DeleteParameters 屬性
此時,我們有一個僅傳回 Products
表中所有記錄的 SqlDataSource 和一個呈現此資料的 GridView。 我們的目標是擴展此範例以允許使用者透過 GridView 刪除產品。 為了實現這一點,我們需要指定 SqlDataSource 控制項 DeleteCommand
和 DeleteParameters
屬性的值,然後配置 GridView 以支援刪除。
DeleteCommand
和 DeleteParameters
屬性可以透過多種方式指定:
- 透過聲明性語法
- 從設計器的屬性視窗
- 從設定資料來源精靈中的指定自訂 SQL 陳述式或預存程序畫面
- 透過「設定資料來源」精靈中「指定檢視表中的資料列」畫面中的「進階」按鈕,實際上會自動產生
DeleteCommand
和DeleteParameters
屬性中使用的DELETE
SQL 陳述式和參數集合
我們將研究如何自動在步驟 2 中建立 DELETE
陳述式。 現在,我們使用設計器中的「屬性」窗口,儘管「配置資料來源」精靈或聲明性語法選項也可以正常運作。
從設計器 InsertUpdateDelete.aspx
中,按一下 ProductsDataSource
SqlDataSource,然後開啟「屬性」視窗 (從「檢視」功能表中,選擇「屬性」窗口,或直接按 F4)。 選擇 DeleteQuery 屬性,這將顯示一組省略號。
圖 2:從「屬性」視窗中選擇「DeleteQuery」屬性
注意
SqlDataSource 沒有DeleteQuery 屬性。 相反,DeleteQuery 是 DeleteCommand
和 DeleteParameters
屬性的組合,並且僅在透過設計器查看視窗時才會在「屬性」視窗中列出。 如果您在來源檢視中查看 DeleteCommand
屬性窗口,您將找到該屬性。
按一下「DeleteQuery」屬性中的省略號以顯示「指令和參數編輯器」對話方塊 (請參閱圖 3)。 從此對話方塊中您可以指定 DELETE
SQL 陳述式並指定參數。 在 DELETE
命令文字方塊中輸入以下查詢 (如果您願意,可以手動或使用查詢產生器):
DELETE FROM Products
WHERE ProductID = @ProductID
接下來,點擊重新整理@ProductID
參數按鈕將參數新增到下面的參數清單中。
@ProductID 參數已新增至 DELETE 指令參數清單。 />
圖 3:從「屬性」視窗中選擇「DeleteQuery」屬性 (點選查看大圖)
不要為此參數提供值 (將其參數來源保留為 None)。 一旦我們向 GridView 添加刪除支持,GridView 將自動提供此參數值,使用點擊刪除按鈕的行的 DataKeys
集合值。
注意
DELETE
查詢中使用的參數名稱 必須 與 GridView、DetailsView 或 FormView 中的 DataKeyNames
值的名稱相同。 也就是說,DELETE
陳述式中的參數是有目的地命名的 @ProductID
(而不是 @ID
) 因為 Products 表中的主鍵列名稱 (以及 GridView 中的 DataKeyNames 值) 是 ProductID
。
如果參數名稱和值不匹配,則 GridView 無法自動為參數指派 DataKeys
集合中的 DataKeyNames
值。
在「命令與參數編輯器」對話方塊中輸入與刪除相關的資訊後,按一下「確定」並前往「來源」檢視以檢查產生的聲明性標記:
<asp:SqlDataSource ID="ProductsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
SelectCommand=
"SELECT [ProductID], [ProductName], [UnitPrice] FROM [Products]"
DeleteCommand="DELETE FROM Products WHERE ProductID = @ProductID">
<DeleteParameters>
<asp:Parameter Name="ProductID" />
</DeleteParameters>
</asp:SqlDataSource>
請注意新增的 DeleteCommand
屬性以及名為 productID
的 <DeleteParameters>
節和參數物件。
配置 GridView 進行刪除
新增 DeleteCommand
屬性後,GridView 的智慧標記現在包含「啟用刪除」選項。 繼續並選取此核取方塊。 如插入、更新和刪除概述中所述,這會導致 GridView 新增一個 CommandField,並將 ShowDeleteButton
屬性設為 true
。 如圖 4 所示,當透過瀏覽器存取該頁面時,會包含一個「刪除」按鈕。 透過刪除一些產品來測試此頁面。
圖 4:每個 GridView 行現在都包含一個刪除按鈕 (點擊可查看全尺寸圖片)
點擊「刪除」按鈕後,將發生回發,GridView 將為 ProductID
參數指派點擊「刪除」按鈕的行的 DataKeys
集合值的值,並呼叫 SqlDataSource Delete()
方法。 然後,SqlDataSource 控制項連接到資料庫並執行 DELETE
陳述式。 然後,GridView 重新綁定到 SqlDataSource,傳回並顯示目前的產品集 (不再包含剛剛刪除的記錄)。
注意
由於 GridView 使用 DataKeys
集合來填入 SqlDataSource 參數,因此將 GridView DataKeyNames
屬性設為構成主鍵的欄位並且 SqlDataSource SelectCommand
傳回這些列至關重要。 此外,將 SqlDataSource 中的參數名稱 DeleteCommand
設為 @ProductID
也很重要。 如果未設定 DataKeyNames
屬性或未命名 @ProductsID
參數,則按一下「刪除」按鈕將導致回發,但實際上不會刪除任何記錄。
圖 5 以圖形方式描述了這種交互作用。 如需從資料 Web 控制項插入、更新和刪除相關的事件鏈的更詳細討論,請參閱檢查與插入、更新和刪除相關的事件教學課程。
圖 5:點選 GridView 中的刪除按鈕呼叫 SqlDataSource Delete()
方法
步驟 2:自動產生 INSERT、UPDATE 和 DELETE 陳述式
如步驟 1 所檢查的,可以透過「屬性」視窗或控制項的聲明語法來指定 INSERT
、UPDATE
和 DELETE
SQL 陳述式。 然而,這種方法需要我們手動編寫 SQL 陳述式,既單調又容易出錯。 幸運的是,設定資料來源精靈提供了一個選項,可以在使用「指定檢視表中的欄位」畫面時自動產生 INSERT
、UPDATE
和 DELETE
陳述式。
讓我們探討一下這個自動產生選項。 將 DetailsView 新增至 InsertUpdateDelete.aspx
的 Designer 中並將 ID
屬性設為 ManageProducts
。 接下來,從 DetailsView ManageProductsDataSource
智慧標記中,選擇建立一個新資料來源並建立一個名為 的 SqlDataSource。
圖 6:建立一個名為 ManageProductsDataSource
的新 SqlDataSource (點擊看大圖)
在「配置資料來源」精靈中,選擇使用 NORTHWINDConnectionString
連接字串,然後按一下「下一步」。 在「配置 Select 陳述式」畫面中,保留選取「指定表或檢視中的列」單選按鈕,然後從下拉清單中選擇 Products
表格。 從核取方塊清單中選擇 ProductID
、ProductName
、UnitPrice
和 Discontinued
列。
圖 7:使用表格,返回 Products
、ProductID
、ProductName
和 UnitPrice
Discontinued
列 (按一下查看全尺寸影像))
若要根據選取的表和列自動產生 INSERT
、UPDATE
和 DELETE
陳述式,請按一下進階按鈕並選取產生 INSERT
、UPDATE
和 DELETE
陳述式核取方塊。
圖 8:選取生成 INSERT
、UPDATE
和 DELETE
陳述式核取方塊
僅當所選表具有主鍵且主鍵列 (或多個列) 包含在傳回列的清單中時,產生 INSERT
、UPDATE
和 DELETE
陳述式核取方塊才可選。 一旦選取了產生 INSERT
、UPDATE
和 DELETE
陳述式核取方塊,使用樂觀並發核取方塊就變為可選狀態,它將擴充結果 WHERE
和 UPDATE
陳述式中的 DELETE
子句以提供樂觀並發控制。 現在,請不要選取此核取方塊;我們將在下一個教學課程中使用 SqlDataSource 控制項檢查樂觀並發。
選取生成 INSERT
、UPDATE
和 DELETE
陳述式核取方塊後,按一下確定返回配置選擇陳述式螢幕,然後按一下下一步和完成,完成配置資料來源精靈。 完成精靈後,Visual Studio 會將 BoundFields 新增至 ProductID
、ProductName
和 UnitPrice
列的 DetailsView 中,並為 Discontinued
列新增 CheckBoxField。 從 DetailsView 的智慧標記中,選取啟用分頁選項,以便造訪此頁面的使用者可以逐步瀏覽產品。 也要清除 DetailsView Width
和 +Height
屬性。
請注意,智慧標記具有可用的「啟用插入」、「啟用編輯」和「啟用刪除」選項。 這是因為 SqlDataSource 包含其 InsertCommand
、UpdateCommand
和 DeleteCommand
的值,如以下聲明性語法所示:
<asp:DetailsView ID="ManageProducts" runat="server" AllowPaging="True"
AutoGenerateRows="False" DataKeyNames="ProductID"
DataSourceID="ManageProductsDataSource" EnableViewState="False">
<Fields>
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True" SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName"
SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Fields>
</asp:DetailsView>
<asp:SqlDataSource ID="ManageProductsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
DeleteCommand=
"DELETE FROM [Products] WHERE [ProductID] = @ProductID"
InsertCommand=
"INSERT INTO [Products] ([ProductName], [UnitPrice], [Discontinued])
VALUES (@ProductName, @UnitPrice, @Discontinued)"
SelectCommand=
"SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
FROM [Products]"
UpdateCommand=
"UPDATE [Products] SET [ProductName] = @ProductName,
[UnitPrice] = @UnitPrice, [Discontinued] = @Discontinued
WHERE [ProductID] = @ProductID">
<DeleteParameters>
<asp:Parameter Name="ProductID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
<asp:Parameter Name="ProductID" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
</InsertParameters>
</asp:SqlDataSource>
請注意 SqlDataSource 控制項如何自動為其 InsertCommand
、UpdateCommand
和 DeleteCommand
屬性設定值。 InsertCommand
和 UpdateCommand
屬性中引用的列集是基於 SELECT
陳述式中的列集。 也就是說,不是在 InsertCommand
和 UpdateCommand
中包含每個 Products 列,而是只有在中指定的那些列 SelectCommand
(由於 ProductID
是一個 IDENTITY
列,其值在編輯時無法更改,並且在插入時自動分配,因此被省略 )。 此外,對於 InsertCommand
、UpdateCommand
和 DeleteCommand
屬性中的每個參數,在 InsertParameters
、UpdateParameters
和 DeleteParameters
集合中都有對應的參數。
若要開啟 DetailsView 的資料修改功能,請選取其智慧標記中的啟用插入、啟用編輯和啟用刪除選項。 這將會新增一個 CommandField,其 ShowInsertButton
、ShowEditButton
和 ShowDeleteButton
屬性設為 true
。
在瀏覽器中造訪該頁面,並注意「詳細資料檢視」中包含的「編輯」、「刪除」和「新建」按鈕。 按一下「編輯」按鈕會將 DetailsView 轉入編輯模式,該模式會將 ReadOnly
屬性設為 false
(預設) 的每個 BoundField 顯示為 TextBox,並將 CheckBoxField 顯示為核取方塊。
圖 9:DetailsView 的預設編輯介面 (點擊看大圖)
同樣,您可以刪除目前選定的產品或將新產品新增至系統。 由於該陳述式僅適用於 InsertCommand
、ProductName
和 UnitPrice
列,因此其他 Discontinued
欄位具有資料庫在插入時指派的預設值 NULL
。 就像 ObjectDataSource 一樣,如果 InsertCommand
缺少任何不允許 NULL
且沒有預設值的資料庫表列,則在嘗試執行 INSERT
陳述式時將出現 SQL 錯誤。
注意
DetailsView 的插入和編輯介面缺乏任何類型的自訂或驗證。 若要新增驗證控製或自訂介面,您需要將 BoundFields 轉換為 TemplateFields。 有關更多資訊,請參閱向編輯和插入介面添加驗證控制項以及自訂資料修改介面教學課程。
另外,請記住,對於更新和刪除,DetailsView 使用目前產品的值,DataKey
值僅在配置 DataKeyNames
屬性時才存在。 如果編輯或刪除似乎沒有效果,請確保設定 DataKeyNames
屬性。
自動產生 SQL 陳述式的局限性
由於產生 INSERT
、UPDATE
和 DELETE
陳述式選項僅在從表中選取列時可用,因此對於更複雜的查詢,您必須編寫自己的 INSERT
、UPDATE
和 DELETE
陳述式,就像我們在步驟 1 中所做的那樣。 通常,SQL SELECT
陳述式使用 JOIN
從一個或多個 Categories
查找表帶回資料以用於顯示目的 (例如在顯示產品資訊時帶回表格的 CategoryName
欄位)。 同時,我們可能希望允許使用者編輯、更新資料或將資料插入核心表 (在本例中 Products
)。
雖然可以手動輸入 INSERT
、UPDATE
和 DELETE
陳述式,但請考慮以下節省時間的提示。 最初設定 SqlDataSource,以便它僅從 Products
表中提取資料。 使用設定資料來源精靈 從資料表或檢視畫面指定列,以便您可以自動產生 INSERT
、UPDATE
和 DELETE
陳述式。 然後,完成精靈後,選擇從「屬性」視窗配置 SelectQuery (或傳回「配置資料來源」精靈,但使用「指定自訂 SQL 陳述式或預存程序」選項)。 然後更新 SELECT
陳述式以包含 JOIN
語法。 該技術提供了自動產生 SQL 陳述式的節省時間的優點,並允許更自訂的 SELECT
陳述式。
自動產生 INSERT
、UPDATE
和 DELETE
陳述式的另一個限制是 INSERT
和 UPDATE
陳述式中的列是基於 SELECT
陳述式傳回的欄位。 但是,我們可能需要更新或插入更多或更少的欄位。 例如,在步驟 2 的範例中,也許我們希望 UnitPrice
BoundField 為唯讀。 在這種情況下,它不應該出現在 UpdateCommand
。 或者我們可能想要設定未出現在 GridView 中的表格欄位的值。 例如,當新增記錄時,我們可能想要將 QuantityPerUnit
值設為 TODO 。
如果需要此類自訂,則需要透過「屬性」視窗、精靈中的「指定自訂 SQL 陳述式或預存程序」選項或透過聲明性語法手動進行。
注意
當新增在資料 Web 控制項中沒有對應欄位的參數時,請記住,需要以某種方式為這些參數值指派值。 這些值可以是: 直接硬編碼在 InsertCommand
或 UpdateCommand
中;可以來自一些預先定義的來源 (查詢字串、會話狀態、頁面上的 Web 控制項等);或者可以透過程式設計方式分配,正如我們在前面的教學課程中看到的那樣。
摘要
為了使資料 Web 控制項能夠利用其內建的插入、編輯和刪除功能,它們所綁定的資料來源控制項必須提供此類功能。 對於 SqlDataSource,這表示 INSERT
、UPDATE
和 DELETE
SQL 陳述式必須指派給 InsertCommand
、UpdateCommand
和 DeleteCommand
屬性。 這些屬性以及相應的參數集合可以手動新增或透過配置資料來源精靈自動產生。 在本教學課程中,我們研究了這兩種技術。
我們在實現樂觀並發教學課程中研究如何使用樂觀並發與 ObjectDataSource。 SqlDataSource 控制項還提供樂觀並發支援。 如步驟 2 所述,當自動產生 INSERT
、UPDATE
和 DELETE
陳述式時,精靈提供了「使用樂觀並發」選項。 正如我們將在下一個教學課程中看到的,將樂觀並發與 SqlDataSource 一起使用會修改 WHERE
、UPDATE
和 DELETE
陳述式中的子句,以確保自資料上次顯示在頁面上以來其他列的值沒有更改。
祝您程式設計愉快!
關於作者
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 找到) 與他聯繫。