你可以處理Select、Update、Insert、Delete和Filter的事件,以驗證并處理傳遞給這些操作的參數值。為了達到這個目標,數據綁定的控件和數據源控件都暴露了適當的事件。例如,在GridView的Updating事件中,你就可以看到Keys、NewValues和OldValues字典中的參數名稱和值,而它們將會被傳遞到數據源。在數據源一端,你可以處理SqlDataSource的Updating事件,看到這些應用到下層命令對象的參數,而這些命令將會執行以完成相關操作。類似的,你可以處理ObjectDataSource的Updating事件來查看或改變參數字典,而這些字典將用于分析UpdateMethod的適當操作。你可以使用這些事件來增加或刪除字典或命令的參數、改變它們的值、或者簡單地驗證參數的輸入格式是否正確。
請注意:你尤其需要驗證Filtering事件的參數輸入,因為在它應用到相關的DataView對象的FilterExpression(過濾器表達式)之前不會獲得SQL編碼(encoded)。
下面的示例演示了處理多個數據控件的事件來枚舉那些通過事件參數傳遞的參數集合。請注意,這個示例把與OrderID主鍵字段相關聯的綁定字段的InsertVisible屬性設置為假,這是因為在下層數據庫中OrderID是一個標識列,不應該傳遞給Insert操作(當插入發生的時候數據庫自動地增加這個值)。同時請注意,在DataKeyNames中,OrderID字段被標記為主鍵,因此這個字段的原始值保留在數據綁定控件所傳遞的Keys字典中。用戶輸入控件的值都傳遞進NewValues字典(除了那些標記了ReadOnly=false的字段)。非鍵字段的原始值由數據綁定控件保留在OldValues字典中,以供傳遞給數據源。這些參數值都被SqlDataSource按照NewValues、Keys和OldValues的次序附加到命令上,盡管在默認情況下,當ConflictDetection被設置為OverwriteChanges的時候,數據源不會附加OldValues。你可以在后面的"使用沖突檢測"部分看到數據源是如何使用OldValues的。
| <script runat="server"> Protected Sub EnumerateDictionary(ByVal dictionary As System.Collections.Specialized.IOrderedDictionary) Dim entry As DictionaryEntry For Each entry In dictionary Response.Write(" <b>" & Server.HtmlEncode(entry.Key) & "</b>=" & Server.HtmlEncode(entry.Value) & " (" & Server.HtmlEncode(entry.Value.GetType().Name) & ")<br />") Next End Sub Protected Sub EnumerateCommandParameters(ByVal command As System.Data.Common.DbCommand) Response.Write("<br/>Parameter order in data source...<br />") Dim param As System.Data.Common.DbParameter For Each param In command.Parameters Response.Write(" <b>" & Server.HtmlEncode(param.ParameterName) & "</b>=" & Server.HtmlEncode(param.Value) & " (" & Server.HtmlEncode(param.Value.GetType().Name) & ")<br />") Next End Sub Protected Sub DetailsView1_ItemUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdateEventArgs) Response.Write("<br/>New Values passed from DetailsView...<br />") EnumerateDictionary(e.NewValues) Response.Write("<br/>Keys passed from DetailsView...<br />") EnumerateDictionary(e.Keys) Response.Write("<br/>Old Values passed from DetailsView...<br />") EnumerateDictionary(e.OldValues) End Sub Protected Sub SqlDataSource1_Updating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) EnumerateCommandParameters(e.Command) e.Cancel = True Response.Write("<br/>Update canceled") End Sub |
你可以通過向數據源使用的參數集合添加靜態的Parameter對象來改變SqlDataSource附加到命令上的參數次序。SqlDataSource會根據這些參數對象的次序來重新排列數據綁定控件所傳遞的參數。當數據源的ProviderName屬性被設置為System.Data.OleDb的時候,這種操作就有用處了,這是由于它不支持命名(named)參數,因此附加到命令上的參數的次序必須與命令中的匿名參數占位符('?')的次序相匹配。當我們使用命名參數的時候,參數的次序就是無關緊要的。你可以指定Parameter對象的Type屬性,確保在執行命令或方法之前,強制數據綁定控件傳遞的值被轉換為適當的數據類型。同樣地,你還可以設置Parameter的Size屬性,規定SqlDataSource命令中DbParameter的位數大小(必須用于輸入/輸出、輸出和返回值參數)。
| <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:NorthwindOLEDB %>" ID="SqlDataSource1" ProviderName="<%$ ConnectionStrings:NorthwindOLEDB.ProviderName %>" runat="server" SelectCommand="SELECT TOP 10 [OrderID], [OrderDate], [ShipCountry] FROM [Orders]" UpdateCommand="UPDATE [Orders] SET [OrderDate] = ?, [ShipCountry] = ? WHERE [OrderID] = ?" OnUpdating="SqlDataSource1_Updating"> <UpdateParameters> 。糰sp:Parameter Name="OrderDate" Type="DateTime" /> 。糰sp:Parameter Name="ShipCountry" Type="String" /> <asp:Parameter Name="OrderID" Type="Int32" /> </UpdateParameters> </asp:SqlDataSource> |
參數命名習慣要求新值根據數據源Select操作所選定的字段來命名。我們也可以通過指定OldValuesParameterFormatString屬性(例如指定為"original_{0}")對Keys或OldValues中的參數進行重命名,以便于把它們和NewValues參數區分開來。你還可以通過處理適當的事件,在數據源操作執行之前改變參數的值,從而自定義參數名稱。例如,如果SqlDataSource的更新操作與一個存儲過程關聯,而該存儲過程使用的參數名稱與默認的命名習慣不同,那么你就可以在該存儲過程被調用之前,在SqlDataSource的Updating事件修改參數名稱。下面的例子演示了這種技術。
| Protected Sub SqlDataSource1_Updating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) e.Command.Parameters("@id").Value = e.Command.Parameters("@ContactID").Value e.Command.Parameters("@name").Value = e.Command.Parameters("@ContactName").Value e.Command.Parameters.Remove(e.Command.Parameters("@ContactID")) e.Command.Parameters.Remove(e.Command.Parameters("@ContactName")) End Sub <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:Contacts %>" ID="SqlDataSource1" runat="server" SelectCommand="SELECT [ContactID], [ContactName] FROM [Contacts]" UpdateCommand="UpdateContactName" UpdateCommandType="StoredProcedure" OnUpdating="SqlDataSource1_Updating"> <UpdateParameters> 。糰sp:Parameter Name="id" Type="Int32" /> <asp:Parameter Name="name" Type="String" /> </UpdateParameters> </asp:SqlDataSource> |
ObjectDataSource不依賴特定的參數次序,而是簡單地查找與參數名稱相匹配的方法。請注意,ObjectDataSource不使用參數的類型或大小來分析方法重載,它僅僅匹配參數名稱,因此,如果你的業務對象中的兩個方法擁有相同的名稱和參數名稱,但是參數類型不同,ObjectDataSource是無法把它們區分開的。你可以在事件中改變ObjectDataSource參數的名稱和值,這點與上面的SqlDataSource示例類似。但是,如果你使用DataObjectTypeName給Update、Insert和Delete操作指定一個特殊的數據對象類型,就不可以修改參數名稱了--只能修改值。如果你需要修改參數名稱,就不要使用DataObjectTypeName,只能用代碼在數據源事件中手動地構造適當的數據對象。
上面我們用到的所有數據源參數都是Input參數,用于把值傳遞到數據源操作中。參數可以是雙向的,例如InputOutput、Output和ReturnValue參數。你可以使用參數對象的Direction屬性來指定參數的方向。如果需要在數據源操作完成之后檢索這些參數的值,就需要處理適當的操作后(post-operation)事件(例如Selected、Updated、Inserted或Deleted事件),從傳遞到這些事件的事件參數中獲取參數值。SqlDataSourceStatusEventArgs擁有Command屬性,你可以使用它來獲取返回值和輸出參數,如下面的例子所示。請注意,對于雙向參數來說,把SqlDataSource中的Parameter對象的Size屬性設置為適當的值是非常重要的。
| <asp:SqlDataSource ID="SqlDataSource1" ……> <SelectParameters> 。糰sp:Parameter Direction="Output" Name="TimeStamp" Type="DateTime" /> 。糰sp:Parameter Direction="ReturnValue" Name="ReturnValue" Type="Int32" /> </SelectParameters> </asp:SqlDataSource> |
為了實現這個目標,ObjectDataSourceStatusEventArgs類型支持OutputParameters集合和ReturnValue屬性,如下面一個例子所示。請注意,在這種情況下,Update操作的返回值是用于檢測操作所影響的行數的。
| Protected Sub ObjectDataSource1_Selected(ByVal sender As Object, ByVal e As ObjectDataSourceStatusEventArgs) Response.Write("Record Count: " & Server.HtmlEncode(e.OutputParameters("totalCount"))) End Sub Protected Sub ObjectDataSource1_Updated(ByVal sender As Object, ByVal e As ObjectDataSourceStatusEventArgs) Response.Write("Rows Affected: " & Server.HtmlEncode(e.ReturnValue) & "<br/>") End Sub <asp:ObjectDataSource ID="ObjectDataSource1" ……> 。糢pdateParameters> 。糰sp:Parameter Name="ContactName" Type="String" /> 。/UpdateParameters> 。糞electParameters> 。糰sp:Parameter Direction="Output" Name="totalCount" Type="Int32" /> </SelectParameters> </asp:ObjectDataSource> |
輸出參數的另一種通常的用途是檢索插入數據庫的行的主鍵值,而該主鍵列是一個標識列(在這種情況下,在插入操作的參數中沒有指定鍵值,該鍵值是在插入操作發生時,數據庫服務器自動生成的)。下面的例子演示了這種技術。
| Protected Sub SqlDataSource1_Inserted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceStatusEventArgs) Response.Write("Record Inserted: " & Server.HtmlEncode(e.Command.Parameters("@ContactID").Value) & "<br/>") End Sub <asp:SqlDataSource ID="SqlDataSource1" ……> …… <InsertParameters> 。糰sp:Parameter Name="contactName" Type="String" /> 。糰sp:Parameter Direction="Output" Name="contactID" Type="Int32" /> 。/InsertParameters> </asp:SqlDataSource> |
