你可以處理Select、Update、Insert、Delete和Filter的事件,以驗證并處理傳遞給這些操作的參數(shù)值。為了達(dá)到這個目標(biāo),數(shù)據(jù)綁定的控件和數(shù)據(jù)源控件都暴露了適當(dāng)?shù)氖录。例如,在GridView的Updating事件中,你就可以看到Keys、NewValues和OldValues字典中的參數(shù)名稱和值,而它們將會被傳遞到數(shù)據(jù)源。在數(shù)據(jù)源一端,你可以處理SqlDataSource的Updating事件,看到這些應(yīng)用到下層命令對象的參數(shù),而這些命令將會執(zhí)行以完成相關(guān)操作。類似的,你可以處理ObjectDataSource的Updating事件來查看或改變參數(shù)字典,而這些字典將用于分析UpdateMethod的適當(dāng)操作。你可以使用這些事件來增加或刪除字典或命令的參數(shù)、改變它們的值、或者簡單地驗證參數(shù)的輸入格式是否正確。
請注意:你尤其需要驗證Filtering事件的參數(shù)輸入,因為在它應(yīng)用到相關(guān)的DataView對象的FilterExpression(過濾器表達(dá)式)之前不會獲得SQL編碼(encoded)。
下面的示例演示了處理多個數(shù)據(jù)控件的事件來枚舉那些通過事件參數(shù)傳遞的參數(shù)集合。請注意,這個示例把與OrderID主鍵字段相關(guān)聯(lián)的綁定字段的InsertVisible屬性設(shè)置為假,這是因為在下層數(shù)據(jù)庫中OrderID是一個標(biāo)識列,不應(yīng)該傳遞給Insert操作(當(dāng)插入發(fā)生的時候數(shù)據(jù)庫自動地增加這個值)。同時請注意,在DataKeyNames中,OrderID字段被標(biāo)記為主鍵,因此這個字段的原始值保留在數(shù)據(jù)綁定控件所傳遞的Keys字典中。用戶輸入控件的值都傳遞進(jìn)NewValues字典(除了那些標(biāo)記了ReadOnly=false的字段)。非鍵字段的原始值由數(shù)據(jù)綁定控件保留在OldValues字典中,以供傳遞給數(shù)據(jù)源。這些參數(shù)值都被SqlDataSource按照NewValues、Keys和OldValues的次序附加到命令上,盡管在默認(rèn)情況下,當(dāng)ConflictDetection被設(shè)置為OverwriteChanges的時候,數(shù)據(jù)源不會附加OldValues。你可以在后面的"使用沖突檢測"部分看到數(shù)據(jù)源是如何使用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 |
你可以通過向數(shù)據(jù)源使用的參數(shù)集合添加靜態(tài)的Parameter對象來改變SqlDataSource附加到命令上的參數(shù)次序。SqlDataSource會根據(jù)這些參數(shù)對象的次序來重新排列數(shù)據(jù)綁定控件所傳遞的參數(shù)。當(dāng)數(shù)據(jù)源的ProviderName屬性被設(shè)置為System.Data.OleDb的時候,這種操作就有用處了,這是由于它不支持命名(named)參數(shù),因此附加到命令上的參數(shù)的次序必須與命令中的匿名參數(shù)占位符('?')的次序相匹配。當(dāng)我們使用命名參數(shù)的時候,參數(shù)的次序就是無關(guān)緊要的。你可以指定Parameter對象的Type屬性,確保在執(zhí)行命令或方法之前,強制數(shù)據(jù)綁定控件傳遞的值被轉(zhuǎn)換為適當(dāng)?shù)臄?shù)據(jù)類型。同樣地,你還可以設(shè)置Parameter的Size屬性,規(guī)定SqlDataSource命令中DbParameter的位數(shù)大小(必須用于輸入/輸出、輸出和返回值參數(shù))。
<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" /> <asp:Parameter Name="ShipCountry" Type="String" /> 。糰sp:Parameter Name="OrderID" Type="Int32" /> </UpdateParameters> </asp:SqlDataSource> |
參數(shù)命名習(xí)慣要求新值根據(jù)數(shù)據(jù)源Select操作所選定的字段來命名。我們也可以通過指定OldValuesParameterFormatString屬性(例如指定為"original_{0}")對Keys或OldValues中的參數(shù)進(jìn)行重命名,以便于把它們和NewValues參數(shù)區(qū)分開來。你還可以通過處理適當(dāng)?shù)氖录,在?shù)據(jù)源操作執(zhí)行之前改變參數(shù)的值,從而自定義參數(shù)名稱。例如,如果SqlDataSource的更新操作與一個存儲過程關(guān)聯(lián),而該存儲過程使用的參數(shù)名稱與默認(rèn)的命名習(xí)慣不同,那么你就可以在該存儲過程被調(diào)用之前,在SqlDataSource的Updating事件修改參數(shù)名稱。下面的例子演示了這種技術(shù)。
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> <asp:Parameter Name="id" Type="Int32" /> 。糰sp:Parameter Name="name" Type="String" /> </UpdateParameters> </asp:SqlDataSource> |
ObjectDataSource不依賴特定的參數(shù)次序,而是簡單地查找與參數(shù)名稱相匹配的方法。請注意,ObjectDataSource不使用參數(shù)的類型或大小來分析方法重載,它僅僅匹配參數(shù)名稱,因此,如果你的業(yè)務(wù)對象中的兩個方法擁有相同的名稱和參數(shù)名稱,但是參數(shù)類型不同,ObjectDataSource是無法把它們區(qū)分開的。你可以在事件中改變ObjectDataSource參數(shù)的名稱和值,這點與上面的SqlDataSource示例類似。但是,如果你使用DataObjectTypeName給Update、Insert和Delete操作指定一個特殊的數(shù)據(jù)對象類型,就不可以修改參數(shù)名稱了--只能修改值。如果你需要修改參數(shù)名稱,就不要使用DataObjectTypeName,只能用代碼在數(shù)據(jù)源事件中手動地構(gòu)造適當(dāng)?shù)臄?shù)據(jù)對象。
上面我們用到的所有數(shù)據(jù)源參數(shù)都是Input參數(shù),用于把值傳遞到數(shù)據(jù)源操作中。參數(shù)可以是雙向的,例如InputOutput、Output和ReturnValue參數(shù)。你可以使用參數(shù)對象的Direction屬性來指定參數(shù)的方向。如果需要在數(shù)據(jù)源操作完成之后檢索這些參數(shù)的值,就需要處理適當(dāng)?shù)牟僮骱螅╬ost-operation)事件(例如Selected、Updated、Inserted或Deleted事件),從傳遞到這些事件的事件參數(shù)中獲取參數(shù)值。SqlDataSourceStatusEventArgs擁有Command屬性,你可以使用它來獲取返回值和輸出參數(shù),如下面的例子所示。請注意,對于雙向參數(shù)來說,把SqlDataSource中的Parameter對象的Size屬性設(shè)置為適當(dāng)?shù)闹凳欠浅V匾摹?/P>
<asp:SqlDataSource ID="SqlDataSource1" ……> <SelectParameters> 。糰sp:Parameter Direction="Output" Name="TimeStamp" Type="DateTime" /> 。糰sp:Parameter Direction="ReturnValue" Name="ReturnValue" Type="Int32" /> </SelectParameters> </asp:SqlDataSource> |
為了實現(xiàn)這個目標(biāo),ObjectDataSourceStatusEventArgs類型支持OutputParameters集合和ReturnValue屬性,如下面一個例子所示。請注意,在這種情況下,Update操作的返回值是用于檢測操作所影響的行數(shù)的。
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" ……> <UpdateParameters> 。糰sp:Parameter Name="ContactName" Type="String" /> 。/UpdateParameters> 。糞electParameters> <asp:Parameter Direction="Output" Name="totalCount" Type="Int32" /> 。/SelectParameters> </asp:ObjectDataSource> |
輸出參數(shù)的另一種通常的用途是檢索插入數(shù)據(jù)庫的行的主鍵值,而該主鍵列是一個標(biāo)識列(在這種情況下,在插入操作的參數(shù)中沒有指定鍵值,該鍵值是在插入操作發(fā)生時,數(shù)據(jù)庫服務(wù)器自動生成的)。下面的例子演示了這種技術(shù)。
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" ……> …… 。糏nsertParameters> <asp:Parameter Name="contactName" Type="String" /> 。糰sp:Parameter Direction="Output" Name="contactID" Type="Int32" /> 。/InsertParameters> </asp:SqlDataSource> |