簡介
正如在?數據插入、更新和刪除概述?中討論的那樣,GridView 控件提供內置的更新和刪除功能,而DetailsView 和 FormView 控件則包含對插入、編輯和刪除功能的支持。這些數據修改功能無需編寫任何代碼,可直接嵌入數據源控件。?數據插入、更新和刪除概述?中探討了如何使用 ObjectDataSource 通過 GridView 、DetailsView 和 FormView 控件完成插入、更新和刪除功能。或者,可使用SqlDataSource 代替 ObjectDataSource 。
回想一下,要使 ObjectDataSource 支持插入、更新和刪除功能,我們需要指定完成插入、更新和刪除操作調用的目標層方法。使用SqlDataSource ,我們需要提供要執行的 INSERT 、UPDATE 、 和DELETE SQL 語句(或存儲過程)。正如我們將在本教程中所看到的,這些語句可手動創建,也可以由 SqlDataSource 的 Configure Data Source 向導自動生成。
注意:由于我們已經討論了 GridView 、DetailsView 和 FormView 控件的插入、編輯和刪除功能,本教程將更多的關注配置 SqlDataSource 控件來支持這些操作。如果您需要溫習在GridView 、DetailsView 和 FormView 內的這些功能,請返回編輯、插入和刪除數據教程,首先從?數據插入、更新和刪除概述?開始。
步驟1 :指定 INSERT 、UPDATE 和 DELETE 語句
正如我們在前面兩個教程中所看到的,要想從SqlDataSource 控件檢索數據,我們需要設置兩個屬性 :
- ConnectionString ,用于指定向哪個數據庫發送查詢。
- SelectCommand ,用于指定返回結果需要執行的 ad-hoc SQL 語句或者存儲過程名稱。
對于帶參數的SelectCommand 值來說,參數值可通過SqlDataSource 的 SelectParameters 集合指定,可包含固定賦值、公用參數源值(查詢字符串字段、會話變量、Web 控件值等),或者通過編碼分配。調用 SqlDataSource 控件的 Select() 方法時(既可以通過編碼調用,也可以從 Web 數據控件自動調用),將建立連接到數據庫的連接,將參數值分配給查詢,并且將命令轉到數據庫。根據控件的DataSourceMode 屬性值,結果隨后可能作為 DataSet 返回,也可能作為 DataReader 返回。
SqlDataSource 控件不僅可以選擇數據,還可以采用完全相同的方式,通過支持INSERT 、UPDATE 和 DELETE SQL 語句用于插入、更新和刪除數據。只需分配要執行的INSERT 、UPDATE 和 DELETE SQL 語句的?InsertCommand?、?UpdateCommand?和DeleteCommand?屬性。如果語句帶有參數(它們中大大多數均帶有參數),請將它們包含到?InsertParameters?、UpdateParameters?和?DeleteParameters?集合中。
一旦指定了InsertCommand 、UpdateCommand 或DeleteCommand 值,相應的 Web 數據 控件的智能標記中的 “Enable Inserting” 、“Enable Editing” 或者 “Enable Deleting” 將變為可用選項。為了說明這一點,我們需要從 Querying.aspx 頁面中舉一個在?使用 SqlDataSource 控件查詢數據?教程中創建的例子,并將其擴充為包含刪除能力。
首先,從SqlDataSource 文件夾中打開 InsertUpdateDelete.aspx 頁面和Querying.aspx 頁面。在 Querying.aspx 頁面的設計器上,從 第一個示例中選擇 SqlDataSource 和 GridView(ProductsDataSource 和 GridView1 控件)。選擇兩個控件之后,轉到 Edit 菜單,并選擇 Copy (或者按下 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 屬性可通過多種方式指定:
- 通過聲明式語法指定
- 從設計器中的 Properties 窗口指定
- 從Configure Data Source 向導的“Specify a custom SQL statement or stored procedure" 屏幕指定
- 從Configure Data Source 向導的“Specify columns from a table of view” 屏幕中的Advanced 按鈕來指定,實際上,它將自動生成DeleteCommand 和 DeleteParameters 屬性中使用的 DELETE SQL 語句和參數集
下面,我們將探討如何在步驟2 中自動創建 DELETE 語句。現在,盡管 Configure Data Source 向導或者聲明式語法選項均可工作,但我們將在設計器中使用 Properties 窗口。
在InsertUpdateDelete.aspx 頁面中的設計器中,單擊 ProductsDataSource SqlDataSource ,隨后屏幕上將顯示 Properties 窗口(從 View 菜單選擇Properties 窗口,或者單擊F4 )。選擇 DeleteQuery 屬性,屏幕上將顯示省略號。
圖2 :從 Properties 窗口選擇 DeleteQuery 屬性
注意:SqlDataSource 沒有 DeleteQuery 屬性。并且,DeleteQuery 是 DeleteCommand 和 DeleteParameters 屬性的組合,僅在使用 設計器 查看窗口Properties 窗口時才被列出。如果您的 Source 視圖中查看 Properties 窗口,您會看到 DeleteCommand 屬性。
DeleteQuery 屬性中單擊省略號,屏幕將顯示Command and Parameter Editor 對話框(見圖 3)。在此對話框中,您可以指定DELETE SQL 語句和參數。在DELETE 命令文本框中輸入下列查詢(既可以手動輸入,也可以使用Query Builder 輸入,如果您愿意的話)。
DELETE FROM Products WHERE ProductID = @ProductID
接下來,單擊 Refresh Parameters 按鈕,向下面的參數列表中添加 @ProductID 參數。
圖3 :從 Properties 窗口選擇 DeleteQuery 屬性
請不要為此參數賦值(保留其參數源為 “None” )。一旦我們給 GridView 提供了刪除支持,GridView 將自動支持此參數值,單擊 Delete 按鈕的行將使用其 DataKeys 集合的值。
注意:DELETE 查詢中使用的參數名稱必須與 GridView 、DetailsView 或者FormView 中的 DataKeyNames 值的名稱一致。也就是說,DELETE 語句中的參數是特意命名為 @ProductID (而不是 @ID),因為 Products 表中關鍵字列名稱(所以,GridView 中的 DataKeyNames 值)為 ProductID 。
如果參數名稱和 DataKeyNames 值不匹配,GridView 將無法從 DataKeys 連接自動分配參數值。
在 Command and Parameter Editor 對話框中輸入刪除相關的信息之后,單擊OK ,轉到Source 視圖,查看結果的聲明標記:
<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 屬性、<DeleteParameters> 部分和名為productID 的參數對象。
配置 GridView 啟用刪除
添加DeleteCommand 屬性之后,GridView 的智能標記將包含 “Enable Deleting” 選項。繼續執行,并選中此復選框。正如在插入、更新和刪除概述?中討論的一樣,這將使GridView 添加一個CommandField ,其 ShowDeleteButton 屬性被設置為 True 。如圖 4 所示,當頁面通過瀏覽器訪問時,頁面上將包含一個 Delete 按鈕。可以通過刪除某些產品來測試此頁面。
圖4 :每個GridView 行均包含一個Delete 按鈕
單擊Delete 按鈕時出現回傳,GridView 為每個單擊 Delete 按鈕行的 ProductID 參數賦值為 DataKeys 集的值,并調用 SqlDataSource 的 Delete() 方法。然后,SqlDataSource 控件將連接到數據庫,并執行DELETE 語句。這樣,GridView 將重新綁定到 SqlDataSource ,恢復并顯示當前產品(這些產品現在不再包含剛剛刪除的記錄)。
注意:由于 GridView 使用其 DataKeys 集合為SqlDataSource 參數賦值,因此 GridView 的 DataKeyNames 屬性可設置為組成主鍵的列并且 SqlDataSource 的 SelectCommand 返回這些列就變得至關重要。此外,SqlDataSource 的 DeleteCommand 中的參數名設置為 @ProductID 也十分重要。如果未設置 DataKeyNames 屬性,或者參數未命名為 @ProductsID ,單擊 Delete 按鈕將導致回傳,但實際上不會刪除任何記錄。
圖5 以圖形方式說明了此過程。有關在 Web 數據控件中與插入、更新和刪除相關的事件鏈的更詳細討論,請參照?探討與插入、更新、刪除相關的事件?教程。
圖5 : 在 GridView 中單擊 Delete 按鈕將調用 SqlDataSource 的 Delete() 方法
步驟2 :自動生成 INSERT 、UPDATE 和 DELETE 語句
正如步驟1 中所討論的,INSERT 、UPDATE 和 DELETE SQL 語句可通過Properties 窗口或者控件的聲明式語法指定。但是,此方法要求我們手動編寫 SQL 語句,這些語句可能十分單調,并且容易產生錯誤。幸運的是 ,Configure Data Source 向導提供了一個選項 , 使用 “ Specify columns from a table of view” 屏幕時可自動生成 INSERT 、UPDATE 和 DELETE 語句。
下面,我們探討一下這個自動生成選項。在InsertUpdateDelete.aspx 中向 設計器 添加一個 DetailsView ,并將其 ID 屬性設置為 ManageProducts 。接下來,從 DetailsView 的智能標記中選擇創建新數據源,并創建一個名為 ManageProductsDataSource 的 SqlDataSource 。
圖6 :創建一個名為 ManageProductsDataSource 的新 SqlDataSource
從Configure Data Source 向導選擇使用NORTHWINDConnectionString 連接字符串,單擊 Next 。從 “Configure the Select Statement” 屏幕,保留 “Specify columns from a table or view” 單選按鈕選中不變,并從下拉列表選擇 Products 表。從復選框列表選擇 ProductID 、 ProductName 、 UnitPrice 和 Discontinued 列。
圖7 :使用 Products 表,返回 ProductID 、ProductName 、UnitPrice 和 Discontinued 列
要根據所選的表和列自動生成 INSERT 、UPDATE 和 DELETE 語句,單擊Advanced 按鈕,選中 “Generate INSERT, UPDATE, and DELETE statements” 復選框。
圖8 :選中 “Generate INSERT, UPDATE, and DELETE statements” 復選框
只有在所選的表有主鍵并且主鍵列包含在返回列的列表中時,“Generate INSERT, UPDATE, and DELETE statements” 復選框才可被選中。在選中 “ Generate INSERT, UPDATE, and DELETE statements” 復選框的情況下,“ Use optimistic concurrency” 復選框將作為結果產生的 UPDATE 和 DELETE 語句中增加 WHERE 子句,提供開放式并發控件。現在,不選中此復選框;我們將在下一篇教程中探討使用 SqlDataSource 控件完成開放式并發。
在選中“Generate INSERT, UPDATE, and DELETE statements” 復選框之后,單擊OK 返回 “Configure Select Statement” 屏幕,然后單擊 Next ,最后單擊 Finish ,完成 Configure Data Source 向導。完成向導后,Visual Studio 將為 ProductID 、ProductName 和 UnitPrice 向 DetailsView 添加BoundFields ,還將為 Discontinued 列添加CheckBoxField 。為了使正在訪問此頁面的用戶能夠查看產品,請從DetailsView 的智能標記選擇 “Enable Paging” 選項。同時,清除DetailsView 的 Width 和 Height 屬性。
請注意,智能標記還提供了 “Enable Inserting” 、“Enable Editing” 和 “Enable Deleting” 選項。這是由于 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 只含有那些SelectCommand 中指定的列,并不是每個Products 列均在InsertCommand 和UpdateCommand 中(由于它是?編輯時無法更改它的值,插入時自動分配,將忽略較小的 ProductID )。此外,對于InsertCommand、UpdateCommand 和DeleteCommand 屬性中的每個參數來說,InsertParameters 、UpdateParameters 和 DeleteParameters 集合中有相應的參數。
要啟用DetailsView 的數據修改功能,請在其智能標記中選中 “Enable Inserting” 、“ Enable Editing” 和 “Enable Deleting” 選項。這將添加一個 CommandField ,其ShowInsertButton 、ShowEditButton 和ShowDeleteButton 屬性均被設置為 True 。
在瀏覽器中訪問頁面,請注意DetailsView 中包含的 Edit 、Delete 和 New 按鈕。單擊 Edit 按鈕,DetailsView 將切換到編輯模式,該模式將所有ReadOnly 屬性被設置為False(默認值)的 BoundField 顯示為一個文本框,而 CheckBoxField 則顯示為復選框。
圖9 :DetailsView 的默認編輯界面
與此類似,您可以刪除當前選擇的產品,或者向系統添加一個新產品。由于 InsertCommand 語句處理 ProductName 、UnitPrice 和 Discontinued 列,其它列在插入時要么由數據庫分配 NULL 值,要么分配它們的默認值。就像 ObjectDataSource 一樣,如果 InsertCommand 丟失了任何不允許使用NULL 值或者沒有默認值的數據庫列表行,嘗試執行 INSERT 語句時將出現一個 SQL 錯誤。
注意:DetailsView 的插入和編輯界面缺少某種定制或驗證。要添加validation 控件或定制界面,您需要將BoundFields 轉換為TemplateFields 。更多信息 , 請參閱?為編輯與插入界面添加驗證控件和?自定義數據修改界面教程。
此外,請記住對于更新和刪除來說,DetailsView 使用當前產品的 DataKey 值,此值僅在 DataKeyNames 已經配置完畢的情況下呈現。如果編輯和刪除看上去似乎沒有效果,請確定是否設置了DataKeyName 屬性。
自動生成 SQL 語句的限制
由于“Generate INSERT, UPDATE, and DELETE statements” 僅在從表中選擇列時可用,對于更復雜的查詢來說,您必須像步驟1 一樣編寫您自己的INSERT 、UPDATE 和 DELETE 語句。通常情況下,為了實現顯示的目的,SQL SELECT 語句使用 JOIN 從一個或者更多個查找表中取回數據(例如,在顯示產品信息時取回 Categories 表的 CategoryName 字段)。同時,我們可能想讓用戶可以編輯、更新或插入數據到“核心”表(此時是 Products 表)。
由于INSERT 、UPDATE 和 DELETE 語句可手動輸入,請考慮下列節省時間的技巧。首先,安裝SqlDataSource ,保證它可以從 Products 表撤回數據。請使用 Configure Data Source 向導的 “ Specify columns from a table or view” 屏幕,保證可以自動生成 INSERT 、UPDATE 和 DELETE 語句,然后,在完成向導之后,從Properties 窗口選擇配置SelectQuery (或者,返回 Configure Data Source 向導,但是使用“Specify a custom SQL statement or stored procedure” 選項。)最后,更新 SELECT 語句,使之包含 JOIN 語法。此技術可以自動生成 SQL 語句,可提供更容易定制的 SELECT 語句,從而節省時間。
自動生成INSERT 、UPDATE 和 DELETE 語句的另外一個限制是 INSERT 和 UPDATE 語句中的列是根據 SELECT 語句返回的列確定的。我們可能需要更新或者插入更多或者更少的字段。例如,來自步驟2 的例子中,我們可能將 UnitPrice BoundField 設置為只讀。在這種情況下,它不應該在UpdateCommand 中出現。或者,我們可能希望設置 GridView 中不出現的表字段。例如,添加新記錄時,我們可能希望將 QuantityPerUnit 設置為“TODO” 。
如果需要這樣的定制,您需要手動完成,要么通過Properties 窗口、向導中的 “Specify a custom SQL statement or stored procedure” 選項完成,要么通過聲明式語法完成。
注意:在 Web 數據控件中添加不含有相應字段的參數時,請記住這些參數值在某些方面需要分配值。分配的值可以為:InsertCommand 或 UpdateCommand 中的直接賦值;可從某些預定義源獲得(查詢字符串 、會話變量、頁面上的 Web? 控件等);或者可通過編碼賦值,正如我們在前面的教程中看到的一樣。
小結
為了使Web 數據控件能夠使用它們內置的插入、編輯和刪除功能,它們綁定的數據源控件必須提供這樣的功能。對SqlDataSource 來說,這意味著INSERT 、UPDATE 和 DELETE SQL 語句必須分配給 InsertCommand 、UpdateCommand 和 DeleteCommand 屬性。這些屬性及其相應的參數集可以手動添加,或者通過Configure Data Source 向導自動生成。在本教程中,我們對上述兩種方法均進行了討論。
在?實現開放式并發?教程中,我們探討了通過 ObjectDataSource 使用開放式并發。SqlDataSource 控件也可提供開放式并發支持。如步驟2 所述,自動生成 INSERT 、UPDATE 和 DELETE 語句時,向導將提供 “ Use optimistic concurrency” 選項。正如下一篇教程中我們將看到的,通過 SqlDataSource 使用開放式并發在 UPDATE 和 DELETE 語句中將修改 WHERE 子句,保證頁面上次顯示數據之后其它列的值均未發生變化。
快樂編程!