一、Render UI
1 GridView
GridView 控件用來在表中顯示數(shù)據(jù)源的值。每列表示一個(gè)字段,而每行表示一條記錄。GridView 控件支持下面的功能:
*
綁定至數(shù)據(jù)源控件,如 SqlDataSource。
*
內(nèi)置排序功能。
*
內(nèi)置更新和刪除功能。
*
內(nèi)置分頁(yè)功能。
*
內(nèi)置行選擇功能。
*
以編程方式訪問 GridView 對(duì)象模型以動(dòng)態(tài)設(shè)置屬性、處理事件等。
*
多個(gè)鍵字段。
*
用于超鏈接列的多個(gè)數(shù)據(jù)字段。
*
可通過主題和樣式進(jìn)行自定義的外觀。
實(shí)驗(yàn)一:
將DetailsView和GridView放置在一個(gè)頁(yè)面。然后在DetailsView的SqlDataSource中的查詢參數(shù)設(shè)置為從GridView傳遞過來。
預(yù)期結(jié)果:當(dāng)GridView中的選擇不同的行時(shí),作為DataKeyNames屬性中的第一個(gè)字段au_id應(yīng)該作為參數(shù)傳遞給DetailsView的SqlDataSource,DetailsView根據(jù)數(shù)據(jù)源Render。
現(xiàn)象:當(dāng)GridView中的選擇不同的行時(shí),DetailsView控件沒有展現(xiàn)數(shù)據(jù)。
分析:
首先懷疑是DetailsView的屬性沒有設(shè)置正確,于是將其數(shù)據(jù)源的查詢參數(shù)設(shè)置默認(rèn)值,結(jié)果能展現(xiàn)。說明DetailsView和其SqlDataSource的屬性設(shè)置正確。
然后懷疑是GridView選擇行時(shí),其SelectedValue為空。于是在其SelectedIndexChanging事件和 SelectedIndexChanged中進(jìn)行調(diào)試,發(fā)現(xiàn)第一次選擇時(shí)GridView的SelectedIndexChanging事件中 SelectedValue確實(shí)為Null,而SelectedIndexChanged事件中SelectedValue值為預(yù)期值。而第二次以及以后的選擇時(shí)兩個(gè)事件中SelectedValue的值都為預(yù)期值。
那么GridView的SelectedValue為什么是Null呢?我查看一下GridView的屬性,EnableViewState設(shè)置為false,于是改成True,再試,還是不行。
經(jīng)過長(zhǎng)時(shí)間的嘗試,發(fā)現(xiàn)在如下兩種情況下程序結(jié)果是對(duì)的:
一:先設(shè)置DetailsView使用的SqlDataSource中查詢參數(shù)的默認(rèn)值,這樣DetailView會(huì)顯示出來,然后將其切換到Edit模式,發(fā)現(xiàn)其各字段的值確實(shí)是GridView選中行的值,然后再點(diǎn)擊Cancel放棄修改。則DetailView能夠正確響應(yīng)GridView的選擇操作。再調(diào)試代碼,發(fā)現(xiàn)GridView的SelectedIndexChanging事件中SelectedValue值不再為空,而是預(yù)期值。
二:發(fā)現(xiàn)一個(gè)更奇特的現(xiàn)象,在SelectedIndexChanging中設(shè)置斷點(diǎn),發(fā)現(xiàn)程序運(yùn)行進(jìn)來,然后什么都不做,跳出事件處理(如下)
protected void GridView1_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
...{
}
程序結(jié)果也是對(duì)的,但是如果不設(shè)置斷點(diǎn),直接運(yùn)行,則結(jié)果不對(duì)。
又經(jīng)過很長(zhǎng)時(shí)間的嘗試,沒有辦法了,只好找同事幫忙。剛開始他也覺得離奇。先跟蹤事件發(fā)生的順序,接著發(fā)現(xiàn)了一個(gè)更離奇的現(xiàn)象:在 SelectedIndexChanging事件中設(shè)置斷點(diǎn)時(shí)我添加了對(duì)GridView.SelectedValue的監(jiān)視。雖然在事件響應(yīng)代碼中我什么都沒有做,但是這個(gè)watch好像起了作用。因?yàn)樗盐姨砑拥倪@個(gè)監(jiān)視去掉后,錯(cuò)誤現(xiàn)象又重現(xiàn)了。而添加這個(gè)監(jiān)視,或者在代碼里這樣寫
string s = GridView1.SelectedValue.ToString();
才能正常運(yùn)行。
最后,同事讓我在工程里新建一個(gè)頁(yè)面,SampleCode拷到這個(gè)頁(yè)面,然后運(yùn)行。以確定不是我的環(huán)境有錯(cuò)誤。然后把SamlpeCode和我的代碼做了對(duì)比。把一些不一樣的屬性去掉。當(dāng)改到GridView的SqlDataSource的EnableViewSate屬性時(shí),“奇跡”出現(xiàn)了。 SampleCode的該屬性設(shè)置為True,而我的是false,我改成True后,程序正常運(yùn)行。
這時(shí)候再去測(cè)試SelectedValue的值,發(fā)現(xiàn)第一次選擇時(shí),在SelectedIndexChanging事件中SelectedValue仍然為null,和錯(cuò)誤時(shí)的現(xiàn)象沒有什么區(qū)別。
錯(cuò)誤的源頭總算找到了,但是SqlDataSource的EnableViewState究竟做了什么呢?GridView的SelectedValue和SqlDataSource有關(guān)嗎?
實(shí)驗(yàn)二:分頁(yè)排序
雖然 GridView、DetailsView 和 FormView 提供頁(yè)導(dǎo)航 UI 的默認(rèn)呈現(xiàn),但是也可以通過設(shè)置 PagerTemplate 屬性自定義頁(yè)導(dǎo)航的呈現(xiàn)。在該模板中,您可以放置 CommandName 屬性設(shè)置為 Page 并且 CommandArgument 屬性設(shè)置為 First、Prev、 Next、Last 或 <number>(其中 <number> 是特定頁(yè)索引的值)的 Button 控件。
應(yīng)用分頁(yè)模板:
<PagerTemplate>
<div style="float: right">
<asp:RangeValidator ID="rvTarget" runat="server" ControlToValidate="txtTarget" Type="Integer"
ErrorMessage="頁(yè)碼超出范圍" MinimumValue="1" MaximumValue='<%#gvReportForDept.PageCount%>'></asp:RangeValidator>
共<%=gvReportForDept.PageIndex + 1%>/<%=gvReportForDept.PageCount%>頁(yè) 第<asp:TextBox
runat="server" ID="txtTarget" onkeypress="searchForDept(event);" CssClass="gotopage" MaxLength="10" Width="25px"
Text='<%#gvReportForDept.PageIndex + 1%>'></asp:TextBox>頁(yè)
<asp:ImageButton runat="server" ID="ibtnGo" CommandName="Page" CommandArgument="-1"
ImageUrl="~/Images/dg_btn_go.gif" />
<asp:ImageButton runat="server" ID="ibtnFirst" CommandName="Page" CommandArgument="First"
ImageUrl="~/Images/dg_btn_le.gif" />
<asp:ImageButton runat="server" ID="ibtnPrev" CommandName="Page" CommandArgument="Prev"
ImageUrl="~/Images/dg_btn_l.gif" />
<asp:ImageButton runat="server" ID="ibtnNext" CommandName="Page" CommandArgument="Next"
ImageUrl="~/Images/dg_btn_r.gif" />
<asp:ImageButton runat="server" ID="ibtnLast" CommandName="Page" CommandArgument="Last"
ImageUrl="~/Images/dg_btn_re.gif" />
</div>
</PagerTemplate>
這將啟用默認(rèn)的分頁(yè)事件。如果需要在分頁(yè)中控制邏輯,例如:如果在TextBox中輸入頁(yè)碼,然后點(diǎn)擊Go按鈕,則不僅需要客戶端驗(yàn)證輸入的范圍,還需要在翻頁(yè)時(shí)驗(yàn)證頁(yè)碼的合理性,所以在PageIndexChanging事件中添加如下代碼:
protected void gvReportForOrder_PageIndexChanging(object sender, GridViewPageEventArgs e)
...{
GridViewPagingHandler(sender, e, this.gvReportForOrder);
}
/**//// <summary>
/// 處理頁(yè)面內(nèi)GrivView的翻頁(yè)事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="gv">GrivView</param>
public void GridViewPagingHandler(object sender, GridViewPageEventArgs e, GridView gv)
...{
if ((e.NewPageIndex >= 0) && (e.NewPageIndex < gv.PageCount))
...{
gv.PageIndex = e.NewPageIndex;
TextBox target = (TextBox)gv.BottomPagerRow.FindControl("txtTarget");
target.Text = Convert.ToString(gv.PageIndex + 1);
}
else if (e.NewPageIndex == -2) // Go button
...{
TextBox target = (TextBox)gv.BottomPagerRow.FindControl("txtTarget");
int goPage;
if (Int32.TryParse(target.Text, out goPage))
...{
if (goPage >= 1 && goPage <= gv.PageCount)
...{
gv.PageIndex = goPage - 1;
}
else
...{
e.Cancel = true;
return;
}
}
}
else
...{
e.Cancel = true;
return;
}
try
...{
//Bind DataSource To GridView
}
catch
...{
e.Cancel = true;
}
}
2 DetailsView
DetailsView 控件用來在表中顯示來自數(shù)據(jù)源的單條記錄,其中記錄的每個(gè)字段顯示在表的一行中。它可與 GridView 控件結(jié)合使用,以用于主/詳細(xì)信息方案。DetailsView 控件支持下面的功能:
*
綁定至數(shù)據(jù)源控件,如 SqlDataSource。
*
內(nèi)置插入功能。
*
內(nèi)置更新和刪除功能。
*
內(nèi)置分頁(yè)功能。
*
以編程方式訪問 DetailsView 對(duì)象模型以動(dòng)態(tài)設(shè)置屬性、處理事件等。
*
可通過主題和樣式進(jìn)行自定義的外觀。
實(shí)驗(yàn)一:應(yīng)用DetailsView的模板列
DetailsView除了已經(jīng)定義的列外,還有模板列,這和FormView的模板類似,但是又有所不同。
在DetailsView'中可以這樣應(yīng)用模板列
<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="ObjectDataSource2"
Height="50px" Width="125px" AutoGenerateEditButton="True" DataKeyNames="au_id"
AutoGenerateRows="False">
<Fields>
<asp:TemplateField HeaderText="Author">
<ItemTemplate>
.......
</ItemTemplate>
<EditItemTemplate>
.........
</EditItemTemplate>
<InsertItemTemplate>
..........
</InsertItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
而FormView中這樣應(yīng)用模板
<asp:FormView ID="fvDatumDetailX" runat="server" DataSourceID="odsDatumDetailX" DataKeyNames="ID" OnItemInserted="fvDatumDetailX_inserted" OnItemCommand="fvDatumDetailX_itemCommand" OnItemUpdated="fvDatumDeatailX_updated">
<ItemTemplate>
........
</ItemTemplate>
<EditItemTemplate>
.........
</EditItemTemplate>
<InsertItemTemplate>
..........
</InsertItemTemplate>
</asp:FormView>
看起來在ItemTemplate節(jié)點(diǎn)下的內(nèi)容似乎是一樣的。但是運(yùn)行起來發(fā)現(xiàn)不一樣:
可以看出:DetailsView只適合對(duì)一個(gè)字段運(yùn)用模板,而FormView則適合對(duì)整條記錄運(yùn)用模板。
結(jié)論:
DetailsView和FormView比較:
相同點(diǎn):
DetailsView和FormView都是對(duì)一條記錄進(jìn)行展現(xiàn)。
不同點(diǎn):雖然可以靈活運(yùn)用DetailsView對(duì)一個(gè)字段運(yùn)用模板的能力使UI看起來好像是對(duì)整條記錄運(yùn)用的模板。但是我們沒有理由這樣做。
也就是說當(dāng)以表格的形式方方正正的展示一條記錄時(shí)可以使用DetailsView來展示,并且字段的值可以使用模板來展示的比較漂亮。但是字段的值和字段的Text,必定是在同一行的。而FormView則可以更靈活的來布局。
DetailsView 和 FormView 之間的主要差異在于 DetailsView 具有內(nèi)置的表格呈現(xiàn)方式,而 FormView 需要用戶定義的模板用于呈現(xiàn)。FormView 和 DetailsView 對(duì)象模型在其他方面非常類似。
DetailsView和GridView的模板列使用是基本一樣的,唯一的區(qū)別是一個(gè)橫向的展示數(shù)據(jù),一個(gè)縱向的展示數(shù)據(jù)。
3 FormView
Note:關(guān)于數(shù)據(jù)綁定
Eval 單向綁定:數(shù)據(jù)是只讀的
Bind 雙向綁定:數(shù)據(jù)可以更改,并返回服務(wù)器端,服務(wù)器可以處理更改后的數(shù)據(jù),如存入數(shù)據(jù)庫(kù).
<%#Eval("Field")>或者<%#expression(Eval("Field")) 用來綁定數(shù)據(jù), <Eval=expression> 用來計(jì)算表達(dá)式。
簡(jiǎn)單屬性 客戶:<%# custID %>
集合 訂單:<asp:ListBox id="List1" datasource='<%# myArray %>' runat="server">
表達(dá)式 合同:<%# ( customer.FirstName + " " + customer.LastName ) %>
方法結(jié)果: 未結(jié)余額:<%# GetBalance(custID) %>
雖然該語法看起來與 Response.Write 的 ASP 快捷方式 <%= %> 相似,但其行為卻大不相同。ASP Response.Write 快捷方式語法在頁(yè)被處理時(shí)計(jì)算,而 ASP.NET 數(shù)據(jù)綁定語法僅在 DataBind 方法被調(diào)用時(shí)計(jì)算。
ASP.NET 頁(yè)框架提供一個(gè)靜態(tài)方法,計(jì)算后期綁定的數(shù)據(jù)綁定表達(dá)式并可選擇將結(jié)果格式化為字符串。DataBinder.Eval 的便利之處在于它消除了開發(fā)人員為了將值強(qiáng)制為所需數(shù)據(jù)類型所必須做的許多顯式強(qiáng)制轉(zhuǎn)換。當(dāng)數(shù)據(jù)綁定控件在模板化的列表中時(shí),它特別有用,因?yàn)橥ǔ?shù)據(jù)行和數(shù)據(jù)字段的類型都必須轉(zhuǎn)換。
請(qǐng)考慮下面的示例,其中一個(gè)整數(shù)將被顯示為貨幣字符串。使用標(biāo)準(zhǔn)的 ASP.NET 數(shù)據(jù)綁定語法,必須先強(qiáng)制轉(zhuǎn)換數(shù)據(jù)行的類型才能檢索數(shù)據(jù)字段 IntegerValue。接著,該值作為參數(shù)被傳遞給 String.Format 方法。
<%# String.Format("{0:c}", ((DataRowView)Container.DataItem)["IntegerValue"]) %>
此語法可能很復(fù)雜,難于記憶。相反,DataBinder.Eval 只是一個(gè)具有三個(gè)參數(shù)的方法:數(shù)據(jù)項(xiàng)的命名容器、數(shù)據(jù)字段名稱和格式字符串。在諸如 FormView、GridView、DetailsView、DataList 或 Repeater 這樣的模板化控件中,命名容器始終為 Container.DataItem。Page 是可用于 DataBinder.Eval 的另一個(gè)命名容器。正如在前一小節(jié)中所討論的,ASP.NET 2.0 還包含 DataBinder.Eval 的新的簡(jiǎn)化語法(僅僅是 Eval),可以在數(shù)據(jù)綁定控件模板中使用它來自動(dòng)解析為 Container.DataItem。
<%# DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:c}") %>
<%# Eval("IntegerValue", "{0:c}") %>
格式字符串參數(shù)是可選的。如果省略它,則 DataBinder.Eval 返回一個(gè)對(duì)象類型的值,如下面的示例所示。
<%# (bool)DataBinder.Eval(Container.DataItem, "BoolValue") %>
要重點(diǎn)注意的是,相對(duì)于標(biāo)準(zhǔn)數(shù)據(jù)綁定語法,DataBinder.Eval 可能導(dǎo)致顯著的性能損失,因?yàn)樗褂煤笃诮壎ǚ瓷?。使?DataBinder.Eval 時(shí)需謹(jǐn)慎,尤其是在不需要字符串格式設(shè)置的時(shí)候。
與 DetailsView 控件一樣,F(xiàn)ormView 通過其關(guān)聯(lián)的數(shù)據(jù)源控件支持自動(dòng) Update、Insert 和 Delete 操作。若要定義編輯或插入操作的輸入 UI,可在定義 ItemTemplate 的同時(shí)定義 EditItemTemplate 或 InsertItemTemplate。在本模板中,您可以對(duì)輸入控件(如 TextBox、CheckBox 或 DropDownList)進(jìn)行數(shù)據(jù)綁定,以綁定到數(shù)據(jù)源的字段。但是,這些模板中的數(shù)據(jù)綁定使用“雙向”數(shù)據(jù)綁定語法,從而允許 FormView 從模板中提取輸入控件的值,以便傳遞到數(shù)據(jù)源。這些數(shù)據(jù)綁定使用新的 Bind(fieldname) 語法而不是 Eval。
重要事項(xiàng): 使用 Bind 語法進(jìn)行數(shù)據(jù)綁定的控件必須設(shè)置有 ID 屬性。
FormView 支持使用 DefaultMode 屬性指定要顯示的默認(rèn)模板,但在默認(rèn)情況下,F(xiàn)ormView 以 ReadOnly 模式啟動(dòng)并呈現(xiàn) ItemTemplate。若要啟用用于從 ReadOnly 模式轉(zhuǎn)換為 Edit 或 Insert 模式的 UI,可以向模板添加一個(gè) Button 控件,并將其 CommandName 屬性設(shè)置為 Edit 或 New。可以在 EditItemTemplate 內(nèi)添加 CommandName 設(shè)置為 Update 或 Cancel 的按鈕,以用于提交或中止更新操作。類似地,也可以添加 CommandName 設(shè)置為 Insert 或 Cancel 的按鈕,以用于提交或中止插入操作。
<asp:FormView DataSourceID="ObjectDataSource1" DataKeyNames="PhotoID" runat="server">
<EditItemTemplate>
<asp:TextBox ID="CaptionTextBox" Text='<%# Bind("Caption") %>' runat="server"/>
<asp:Button Text="Update" CommandName="Update" runat="server"/>
<asp:Button Text="Cancel" CommandName="Cancel" runat="server"/>
</EditItemTemplate>
<ItemTemplate>
<asp:Label Text='<%# Eval("Caption") %>' runat="server" />
<asp:Button Text="Edit" CommandName="Edit" runat="server"/>
</ItemTemplate>
</asp:FormView>
在對(duì) GridView 或 DetailsView 執(zhí)行更新或插入操作時(shí),如果該控件的列或字段定義了 BoundField,GridView 或 DetailsView 負(fù)責(zé)創(chuàng)建 Edit 或 Insert 模式中的輸入 UI,以便它能自動(dòng)提取這些輸入值以傳遞回?cái)?shù)據(jù)源。由于模板包含任意的用戶定義的 UI 控件,因此,需要使用雙向數(shù)據(jù)綁定語法,這樣 FormView 等模板化控件才能知道應(yīng)從模板中提取哪些控件值以用于更新、插入或刪除操作。在 EditItemTemplate 中仍然可以使用 Eval 語法進(jìn)行不傳遞回?cái)?shù)據(jù)源的數(shù)據(jù)綁定。另請(qǐng)注意,F(xiàn)ormView 與 DetailsView 和 GridView 一樣,支持使用 DataKeyNames 屬性保留主鍵字段(即使這些字段并未呈現(xiàn))的原始值以傳遞回更新/插入操作。
實(shí)驗(yàn):Eval VS. Bind
<EditItemTemplate>
<table>
<tr> <td> ID </td>
<td><%#Eval("au_id")%> </td> </tr>
<tr> <td> au_lname </td>
<td> <asp:TextBox ID="TextBox1" runat="server" Text='<%#Bind("au_lname")%>'></asp:TextBox>
</td></tr>
<!---其他
--->
</table>
<EditItemTemplate>
運(yùn)行結(jié)果和預(yù)期結(jié)果一致。
4 Repeater
5 DataList
二、DataSource
1 SqlDataSource
SqlDataSource,后者支持用于指定連接字符串、SQL 語句或存儲(chǔ)過程的屬性,用以查詢或修改數(shù)據(jù)庫(kù)。雖然這適合大多數(shù)小規(guī)模的個(gè)人或業(yè)余站點(diǎn),但對(duì)于較大規(guī)模的企業(yè)級(jí)應(yīng)用程序,在應(yīng)用程序的呈現(xiàn)頁(yè)中直接存儲(chǔ) SQL 語句可能很快就會(huì)變得無法維護(hù)。這些應(yīng)用程序通常需要用中間層數(shù)據(jù)訪問層或業(yè)務(wù)組件構(gòu)成的封裝性更好的數(shù)據(jù)模型。所幸 ASP.NET 數(shù)據(jù)源控件模型使用 ObjectDataSource 控件支持這種方法。
實(shí)驗(yàn):通過SqlDataSource將DetailView與GridView結(jié)合使用
本實(shí)驗(yàn)是在DetailView的練習(xí)中的擴(kuò)展。
當(dāng)時(shí)的場(chǎng)景是通過將 ControlParameter 關(guān)聯(lián)到 GridView 的 SelectedValue 屬性來實(shí)現(xiàn)主/詳細(xì)信息方案。SelectedValue 屬性返回 DataKeyNames 屬性指定的第一個(gè)字段的值。
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ProviderName="<%$ ConnectionStrings:Pubs.ProviderName %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" OnDataBinding="SqlDataSource2_DataBinding" ConnectionString="<%$ ConnectionStrings:Pubs %>"
UpdateCommand="UPDATE [authors] SET [au_id] = @au_id, [au_lname] = @au_lname, [au_fname] = @au_fname, [state] = @state WHERE ([au_id] = @au_id)">
<SelectParameters>
<asp:ControlParameter ControlID="GridView1" Name="au_id" PropertyName="SelectedValue" Type="Object" />
</SelectParameters>
</asp:SqlDataSource>
GridView的DataKeyNames 屬性可以指定用多個(gè)逗號(hào)分隔的字段值,例如,如果需要將多個(gè)值傳遞給主/詳細(xì)信息方案中的詳細(xì)信息數(shù)據(jù)源,便可以這樣做。這些附加鍵字段的值通過 SelectedDataKey 屬性公開,該屬性返回鍵字段的名稱/值對(duì)的 DataKey 對(duì)象。ControlParameter 甚至能夠通過將 PropertyName 屬性設(shè)置為一個(gè)表達(dá)式來引用這些鍵。例如 SelectedDataKey.Values("au_id"))。
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ProviderName="<%$ ConnectionStrings:Pubs.ProviderName %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" OnDataBinding="SqlDataSource2_DataBinding" ConnectionString="<%$ ConnectionStrings:Pubs %>"
UpdateCommand="UPDATE [authors] SET [au_id] = @au_id, [au_lname] = @au_lname, [au_fname] = @au_fname, [state] = @state WHERE ([au_id] = @au_id)">
<SelectParameters>
<asp:ControlParameter ControlID="GridView1" Name="au_id" PropertyName="SelectedDataKey.Values[0]" Type="Object" />
<asp:ControlParameter ControlID="GridView1" Name="au_lname" PropertyName="SelectedDataKey.Values[1]" Type="object" />
</SelectParameters>
</asp:SqlDataSource>
2 ObjectDataSource
ObjectDataSource 控件對(duì)象模型類似于 SqlDataSource 控件。ObjectDataSource 公開一個(gè) TypeName 屬性(而不是 ConnectionString 屬性),該屬性指定要實(shí)例化來執(zhí)行數(shù)據(jù)操作的對(duì)象類型(類名)。類似于 SqlDataSource 的命令屬性,ObjectDataSource 控件支持諸如 SelectMethod、UpdateMethod、InsertMethod 和 DeleteMethod 的屬性,用于指定要調(diào)用來執(zhí)行這些數(shù)據(jù)操作的關(guān)聯(lián)類型的方法。
ObjectDataSource 要求它能夠使用的對(duì)象具有非常具體的設(shè)計(jì)模式。這些限制主要是由執(zhí)行 Web 應(yīng)用程序請(qǐng)求的無狀態(tài)環(huán)境施加的。由于為每個(gè)請(qǐng)求提供服務(wù)通常都要?jiǎng)?chuàng)建和銷毀對(duì)象,通過對(duì)象數(shù)據(jù)源綁定的對(duì)象也需要是無狀態(tài)的。默認(rèn)情況下,ObjectDataSource 假定 TypeName 屬性所指定的類型具有默認(rèn)構(gòu)造函數(shù)(無參數(shù)),不過,通過處理 ObjectCreating 事件來創(chuàng)建自定義對(duì)象實(shí)例并將它指定給事件參數(shù)的 ObjectInstance 屬性,也可以代表 ObjectDataSource 實(shí)例化該類型。與 SelectMethod 屬性關(guān)聯(lián)的對(duì)象方法可以返回任何 Object 或 IEnumerable 列表、集合或數(shù)組。在上面的數(shù)據(jù)訪問層示例中,DataView 對(duì)象實(shí)現(xiàn)了 Ienumerable。正如下一節(jié)將要討論的,這些方法還可能返回強(qiáng)類型集合或?qū)ο蟆?
實(shí)驗(yàn)一:將ObjectDataSource綁定到對(duì)象
步驟:
在App_Code文件夾中建立AuthorDB類
public class AuthorsDB
...{
static string connectionString = ConfigurationManager.ConnectionStrings["Pubs"].ConnectionString;
public AuthorsDB()
...{
}
/**//// <summary>
/// 通過ID獲得詳細(xì)情況,用于綁定DetailsView的select方法
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
public static DataSet GetAuthorsByID(string ID)
...{
IDbConnection dbConnection = new SqlConnection(connectionString);
string queryString = "SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)";
IDbCommand cmd = new SqlCommand();
cmd.CommandText = queryString;
cmd.Connection = dbConnection;
IDataParameter param_ID = new SqlParameter("au_id", ID);
cmd.Parameters.Add(param_ID);
IDbDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
return dataSet;
}
/**//// <summary>
/// 獲得所有ID,用于綁定DropdownList
/// </summary>
/// <returns></returns>
public static DataSet GetIDs()
...{
try
...{
Database db_Pubs = DatabaseFactory.CreateDatabase();
DbCommand cmd = db_Pubs.GetSqlStringCommand(@"SELECT [au_id] FROM [authors]");
DataSet result = db_Pubs.ExecuteDataSet(cmd);
return result;
}
catch (Exception ex)
...{
throw new Exception("無法獲取。。。", ex);
}
}
/**//// <summary>
/// 用于綁定DetailView的更新方法
/// </summary>
/// <param name="au_id"></param>
/// <param name="au_lname"></param>
/// <param name="au_fname"></param>
/// <param name="state"></param>
public static void UpdateAuthor(string au_id, string au_lname, string au_fname, string state)
...{
int rowAffected = 0;
Database db_Pubs = DatabaseFactory.CreateDatabase();
DbCommand cmd = db_Pubs.GetSqlStringCommand(@"UPDATE [authors] SET [au_lname]=@au_lname, [au_fname]=@au_fname, [state]=@state WHERE ([authors].[au_id] = @au_id)");
db_Pubs.AddInParameter(cmd, "au_id", DbType.String, au_id);
db_Pubs.AddInParameter(cmd, "au_lname", DbType.String, au_lname);
db_Pubs.AddInParameter(cmd, "au_fname", DbType.String, au_fname);
db_Pubs.AddInParameter(cmd, "state", DbType.String, state);
try
...{
rowAffected = db_Pubs.ExecuteNonQuery(cmd);
}
catch (Exception ex)
...{
throw new Exception("更新失??!", ex);
}
if (rowAffected == 0)
throw new Exception("更新失??!");
}
}
新建DetailsViewDAL.aspx,添加ObjectDataSource,配置數(shù)據(jù)源和相關(guān)方法(碰到ObjectDataSource看不見類文件,重啟VS2005可能可以解決問題)。
結(jié)果:可以正確顯示,但是更新時(shí)出錯(cuò),錯(cuò)誤信息為:
ObjectDataSource 'ObjectDataSource2' could not find a non-generic method 'UpdateAuthor' that has parameters: au_id, au_lname, au_fname, state, phone, address, city, zip, contract.
分析:
從異常信息來看,應(yīng)該是更新時(shí)調(diào)用的方法簽名和AuthorDB中的UpdateAuthor方法的簽名不一致。也就是說,雖然給ObjectDataSource指定了更新方法,但是它調(diào)用的時(shí)候傳參是按照Select出來的列構(gòu)造的。
聯(lián)想在GridView對(duì)數(shù)據(jù)的綁定,猜想在DetailsView中設(shè)置綁定列的 ReadOnly屬性,應(yīng)該可以使不需要更改的列不被生成參數(shù)(這也更符合實(shí)際中的需要,不可編輯的就不需要更新)。這時(shí)發(fā)現(xiàn)au_id也不會(huì)被作為參數(shù)傳遞。于是設(shè)置DataKeyNames="au_id",這樣ObjectDataSource會(huì)根據(jù) OldValuesParameterFormatString="original_{0}“來生成一個(gè)參數(shù)original_au_id,這樣調(diào)整 UpdateAuthor中的參數(shù)即可。
<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="ObjectDataSource2"
Height="50px" Width="125px" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateRows="False">
<Fields>
<asp:BoundField HeaderText="ID" DataField="au_id" ReadOnly="true"/>
<asp:BoundField HeaderText="au_lname" DataField="au_lname" />
<asp:BoundField HeaderText="au_fname" DataField="au_fname" />
<asp:BoundField HeaderText="phone" DataField="phone" ReadOnly="True"/>
<asp:BoundField HeaderText="address" DataField="address" ReadOnly="True"/>
<asp:BoundField HeaderText="city" DataField="city" ReadOnly="True"/>
<asp:BoundField HeaderText="state" DataField="state" />
<asp:BoundField HeaderText="zip" DataField="zip" ReadOnly="True"/>
<asp:CheckBoxField HeaderText="contract" DataField="contract" ReadOnly="True"/>
</Fields>
</asp:DetailsView>
結(jié)果:
實(shí)現(xiàn)了部分?jǐn)?shù)據(jù)可以編輯,部分?jǐn)?shù)據(jù)只讀。
SqlDataSource和ObjectDataSource的比較
ObjectDataSourceStatusEventArgs支持的屬性:
AffectedRows Gets or sets the number of rows that are affected by the data operation.
Exception Gets a wrapper for any exceptions that are thrown by the method that is called by the ObjectDataSource control during a data operation.
ExceptionHandled Gets or sets a value indicating whether an exception that was thrown by the business object has been handled.
OutputParameters Gets a collection that contains business object method parameters and their values.
ReturnValue Gets the return value that is returned by the business object method, if any, as an object.
SqlDataSourceStatusEventArgs支持的屬性:
Name Description
AffectedRows Gets the number of rows affected by a database operation.
Command Gets the database command submitted to the database.
Exception Gets a wrapper for any exceptions thrown by the database during a data operation.
ExceptionHandled Gets or sets a value indicating whether an exception thrown by the database has been handled.
關(guān)于AffectedRows:
SqlDataSource在更新后 SqlDataSourceStatusEventArgs有AffectedRows來指示相關(guān)Command影響的行數(shù),而 ObjectDataSource的ObjectDataSourceStatusEventArgs除了AffectedRows屬性外,還有 ReturnValue來判斷相關(guān)操作所調(diào)用的方法的返回值。
但是不同之處在于,SqlDataSourceStatusEventArgs的AffectedRows屬性通常都能正確的反映Command影響的行數(shù),而ObjectDataSourceStatusEventArgs的AffectedRows確總是為-1。
而DetailsView的 DetailsViewUpdatedEventArgs的AffectedRows確是在執(zhí)行沒有發(fā)生異?;蛘甙l(fā)生異常但是已經(jīng)處理時(shí)為 SqlDataSource、ObjectDataSource的AffectedRows,而發(fā)生異常時(shí)為0。
所以:如果我們確實(shí)需要在DetailsView的事件中拿到正確的AffectedRows的值,并且使用了ObjectDataSource,則需要在Updated/Deleted/Inserted事件后手動(dòng)設(shè)置AffectedRows。
protected void ObjectDataSource1_Updated(object sender, ObjectDataSourceStatusEventArgs e)
...{
.........
e.AffectedRows = e.ReturnValue;
}
關(guān)于參數(shù):
對(duì)于參數(shù)的方向,缺省情況下都是Input型,即用于將值傳入數(shù)據(jù)源操作。參數(shù)也可以是雙向的,例如 InputOutput、Output 和 ReturnValue 參數(shù)。可以使用 Parameter 對(duì)象的屬性 Direction 指定參數(shù)的方向性。若要在數(shù)據(jù)源操作完成之后檢索這些參數(shù)的值,應(yīng)處理相應(yīng)的操作后事件,例如 Selected、Updated、Inserted 或 Deleted 事件,以便從傳遞給這些事件的事件參數(shù)中獲得參數(shù)值。SqlDataSourceStatusEventArgs 有一個(gè) Command 屬性,可以使用該屬性獲得返回值和輸出參數(shù)。注意,對(duì)于雙向參數(shù),在 SqlDataSource 中將 Parameter 對(duì)象的 Size 屬性設(shè)置為適當(dāng)?shù)闹岛苤匾?。ObjectDataSourceStatusEventArgs 類型支持 OutputParameters 集合和 ReturnValue 屬性。
SqlDataSourceStatusEventArgs 的Output方向的參數(shù)一般來自存儲(chǔ)過程的output參數(shù)。而ObjectDataSource的Output方向的參數(shù)一般來自一個(gè)相關(guān)方法的一個(gè)用out聲明的參數(shù),例如:
public static int UpdateAuthor(string original_au_id, string au_lname, string au_fname, string state,out int My_ReturnValue)
...{
My_ReturnValue=1;
........
return EffactedRows
}
想象如下場(chǎng)景:ObjectDataSource的更新操作需要調(diào)用一個(gè)存儲(chǔ)過程,而該存儲(chǔ)過程包含一個(gè)output參數(shù),如何返回該參數(shù)呢?
首先,ObjectDataSource必須先聲明該參數(shù),My_ReturnValue的Direction為Output.
<asp:ObjectDataSource ID="ObjectDataSource2" runat="server" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetAuthorsByID" TypeName="AuthorsDB" UpdateMethod="UpdateAuthor" OnUpdated="ObjectDataSource2_Updated">
<UpdateParameters>
<asp:Parameter Name="au_id" Type="String" />
<asp:Parameter Name="au_lname" Type="String" />
<asp:Parameter Name="au_fname" Type="String" />
<asp:Parameter Name="state" Type="String" />
<asp:Parameter Direction="Output" Name="My_ReturnValue" Type="Int32" />
</UpdateParameters>
<SelectParameters>
<asp:ControlParameter ControlID="DropDownList1" Name="ID" PropertyName="SelectedValue"
Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
然后,更新方法需要聲明out參數(shù),并且給該參數(shù)賦值。
public static int UpdateAuthor(string original_au_id, string au_lname, string au_fname, string state,out int My_ReturnValue)
...{
My_ReturnValue = -1;
int rowAffected = 0;
Database db_Pubs = DatabaseFactory.CreateDatabase();
DbCommand cmd = db_Pubs.GetSqlStringCommand(@"update [authors] set [au_lname]=@au_lname, [au_fname]=@au_fname, [state]=@state where ([authors].[au_id] = @au_id)
set @my_returnvalue=3");
// DbCommand cmd = db_Pubs.GetStoredProcCommand("MyTest");
db_Pubs.AddInParameter(cmd, "au_id", DbType.String, original_au_id);
db_Pubs.AddInParameter(cmd, "au_lname", DbType.String, au_lname);
db_Pubs.AddInParameter(cmd, "au_fname", DbType.String, au_fname);
db_Pubs.AddInParameter(cmd, "state", DbType.String, state);
db_Pubs.AddOutParameter(cmd, "My_ReturnValue", DbType.Int32,4);
try
...{
rowAffected = db_Pubs.ExecuteNonQuery(cmd);
My_ReturnValue = (int)db_Pubs.GetParameterValue(cmd, "My_ReturnValue");
}
catch (Exception ex)
...{
throw new Exception("更新失敗!", ex);
}
if (rowAffected == 0)
throw new Exception("更新失??!");
return rowAffected;
}
這樣,在更新之后的事件中
protected void ObjectDataSource2_Updated(object sender, ObjectDataSourceStatusEventArgs e)
...{
e.ExceptionHandled = true;
e.AffectedRows = 1;
}
可以監(jiān)視到 e.OutputParameters["My_ReturnValue"] 3 object {int}。
3 XmlDataSource
4 SitemapDataSource
三、Performance and Security
1 緩存數(shù)據(jù)、頁(yè)面
2 連接字符串常見處理
3 沖突檢測(cè)
SqlDataSource和ObjectDataSource都支持ConflictDetection屬性。該屬性設(shè)置為CompareAllValues,則OldValues會(huì)被傳遞,否則不會(huì),而只是傳遞Key和NewValues。
所以,對(duì)于ObjectDataSource,該屬性的設(shè)置將直接影響到相應(yīng)方法的簽名。所以如果設(shè)置錯(cuò)誤,將會(huì)在編譯時(shí)就報(bào)異常。而SqlDataSource則只會(huì)影響到相應(yīng)Command的正確性,而不會(huì)報(bào)錯(cuò)誤。如
SqlDataSource這樣設(shè)置
<asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:Northwind %>" ID="SqlDataSource1"
runat="server" SelectCommand="SELECT [OrderID], [OrderDate], [ShipCountry] FROM [Orders]"
UpdateCommand="UPDATE [Orders] SET [OrderDate] = @OrderDate, [ShipCountry] = @ShipCountry WHERE [OrderID] = @original_OrderID AND [OrderDate] = @original_OrderDate AND [ShipCountry] = @original_ShipCountry"
ConflictDetection="OverwriteChanges" OldValuesParameterFormatString="original_{0}"
OnUpdating="SqlDataSource1_Updating">
<UpdateParameters>
<asp:Parameter Name="OrderDate" Type="DateTime" />
<asp:Parameter Name="ShipCountry" Type="String" />
<asp:Parameter Name="original_OrderID" Type="Int32" />
<asp:Parameter Name="original_OrderDate" Type="DateTime" />
<asp:Parameter Name="original_ShipCountry" Type="String" />
</UpdateParameters>
</asp:SqlDataSource>
執(zhí)行的時(shí)候用Sql Server Profiler抓到的語句如下
exec sp_executesql N'UPDATE [Orders] SET [OrderDate] = @OrderDate, [ShipCountry] = @ShipCountry WHERE [OrderID] = @original_OrderID AND
[OrderDate] = @original_OrderDate AND [ShipCountry] = @original_ShipCountry',N'@OrderDate datetime,@ShipCountry nvarchar(6),@original_OrderID
int,@original_OrderDate datetime,@original_ShipCountry nvarchar(4000)',@OrderDate=''8888-07-05
00:00:00:000'',@ShipCountry=N'France',@original_OrderID=10248,@original_OrderDate=NULL,@original_ShipCountry=NULL
Key和NewValues被傳遞了過來,但是OldValues則為NULL,所以更新不成功,但是不會(huì)出錯(cuò)
而將CompareAllValues屬性設(shè)置為CompareAllValues后抓到的語句如下:
exec sp_executesql N'UPDATE [Orders] SET [OrderDate] = @OrderDate, [ShipCountry] = @ShipCountry WHERE [OrderID] = @original_OrderID AND
[OrderDate] = @original_OrderDate AND [ShipCountry] = @original_ShipCountry',N'@OrderDate datetime,@ShipCountry nvarchar(6),@original_OrderID
int,@original_OrderDate datetime,@original_ShipCountry nvarchar(6)',@OrderDate=''7777-07-05
00:00:00:000'',@ShipCountry=N'France',@original_OrderID=10248,@original_OrderDate=''6666-07-05 00:00:00:000'',@original_ShipCountry=N'France'
可以發(fā)現(xiàn)OldValues被傳遞了過來。
不過需要注意的是:不管設(shè)置為OverwriteChanges還是CompareAllValues,在SqlDataSourceCommandEventArgs中都能拿到所有值(即OldValues也能拿到)。
另外,ObjectDataSource的DataObjectTypeName可以和CompareAllValues 結(jié)合使用。這樣ObjectDataSource構(gòu)造參數(shù)時(shí)會(huì)去調(diào)用DataObjectTypeName指定類的構(gòu)造函數(shù)。當(dāng)更新、刪除,插入操作時(shí)會(huì)調(diào)用以該類作為參數(shù),并且正好兩個(gè)參數(shù)的方法。這樣可以方便編寫代碼。
public class Contact
...{
private int _contactID;
public int ContactID
...{
get ...{ return _contactID; }
set ...{ _contactID = value; }
}
private String _contactName;
public String ContactName
...{
get ...{ return _contactName; }
set ...{ _contactName = value; }
}
public Contact()
...{
//
// TODO: Add constructor logic here
//
}
public Contact(int ContactID, String ContactName)
...{
_contactID = ContactID;
_contactName = ContactName;
}
}
public int UpdateContact(Contact c, Contact original_c)
...{
HttpContext.Current.Response.Write("UpdateContact(Contact c, Contact original_c)");
return UpdateContact(c.ContactName, original_c.ContactID, original_c.ContactName);
}
<asp:ObjectDataSource TypeName="ContactsListOptimistic" ID="ObjectDataSource1" runat="server"
SelectMethod="GetContacts" UpdateMethod="UpdateContact" ConflictDetection="CompareAllValues"
OldValuesParameterFormatString="original_{0}" DataObjectTypeName="Contact" OnUpdated="ObjectDataSource1_Updated">
<UpdateParameters>
<asp:Parameter Name="c" />
<asp:Parameter Name="original_c" />
</UpdateParameters>
</asp:ObjectDataSource>
四、其他
1 處理空值
數(shù)據(jù)控件支持各種處理空數(shù)據(jù)或缺少數(shù)據(jù)的方法。首先,GridView、FormView 和 DetailsView 全都支持一個(gè) EmptyDataText 或 EmptyDataTemplate 屬性,當(dāng)數(shù)據(jù)源沒有返回?cái)?shù)據(jù)行時(shí),可以使用該屬性為控件指定一種呈現(xiàn)。只需設(shè)置 EmptyDataText 和 EmptyDataTemplate 之一(當(dāng)同時(shí)設(shè)置兩者時(shí),EmptyDataTemplate 優(yōu)先)。還可以在 BoundField(以及派生字段類型)、TemplateField 或數(shù)據(jù)源參數(shù)對(duì)象上指定一個(gè) ConvertEmptyStringToNull 屬性,以指定在調(diào)用關(guān)聯(lián)的數(shù)據(jù)源操作之前,應(yīng)將從客戶端發(fā)送的 String.Empty 值轉(zhuǎn)換為空值。ObjectDataSource 還支持一個(gè) ConvertNullToDbNull 屬性,當(dāng)關(guān)聯(lián)的方法需要 DbNull 參數(shù)而不是空值時(shí),可將該屬性設(shè)置為 true(Visual Studio 數(shù)據(jù)集中的 TableAdapter 類有此要求)。還可以在 BoundField(以及派生字段類型)上指定一個(gè) NullDisplayText 屬性,以便在數(shù)據(jù)源中返回的字段值為空值時(shí),為該字段指定要顯示的值。如果該值在編輯模式期間未更改,則它將在一個(gè) Update 操作期間作為空值返回?cái)?shù)據(jù)源。最后,還可以在數(shù)據(jù)源參數(shù)上指定一個(gè) DefaultValue 屬性,以便在傳遞的參數(shù)值為空值時(shí),為參數(shù)指定一個(gè)默認(rèn)值。這些屬性將按順序應(yīng)用;例如如果同時(shí)設(shè)置 ConvertEmptyStringToNull 和 DefaultValue,則 String.Empty 值首先被轉(zhuǎn)換為空值,隨后被轉(zhuǎn)換為默認(rèn)值。
2 處理事件
數(shù)據(jù)控件事件旨在為您提供在頁(yè)面執(zhí)行生命周期中可插入自己的自定義代碼的適當(dāng)位置。數(shù)據(jù)控件一般在特定操作發(fā)生之前和之后公開事件。在操作之前激發(fā)的事件通常使用 -ing 后綴進(jìn)行命名,而在操作之后激發(fā)的事件則使用 -ed 后綴進(jìn)行命名。例如,GridView 支持的事件包括:
* PageIndexChanging 和 PageIndexChanged -- 在分頁(yè)操作之前和之后引發(fā)
* SelectedIndexChanging 和 SelectedIndexChanged -- 在選擇操作之前和之后引發(fā)
* Sorting 和 Sorted -- 在排序操作之前和之后引發(fā)
* RowEditing 和 RowCancelingEdit -- 在行進(jìn)入編輯模式之前或在編輯模式被取消之前引發(fā)
* RowUpdating 和 RowUpdated -- 在更新操作之前和之后引發(fā)
* RowDeleting 和 RowDeleted -- 在刪除操作之前和之后引發(fā)
* RowDataBound -- 當(dāng)行與數(shù)據(jù)綁定時(shí)引發(fā)
* RowCreated -- 當(dāng)創(chuàng)建行用于呈現(xiàn)(作為 TableRow)時(shí)引發(fā)
* RowCommand -- 當(dāng)從控件中激發(fā)按鈕命令時(shí)引發(fā)
數(shù)據(jù)源控件也公開事件,類似于數(shù)據(jù)綁定控件事件。SqlDataSource 和 ObjectDataSource 控件都支持下列事件:
* Selecting 和 Selected -- 在選擇操作之前和之后引發(fā)
* Updating 和 Updated -- 在更新操作之前和之后引發(fā)
* Deleting 和 Deleted -- 在刪除操作之前和之后引發(fā)
* Inserting 和 Inserted -- 在插入操作之前和之后引發(fā)
* Filtering -- 在篩選器操作之前引發(fā)
ObjectDataSource 控件還在 TypeName 屬性所指定的對(duì)象被創(chuàng)建或銷毀時(shí)額外公開一些事件。通過設(shè)置所傳遞的事件參數(shù)的 ObjectInstance 屬性,可以在 ObjectCreating 事件中實(shí)際設(shè)置一個(gè)自定義對(duì)象。
* ObjectCreating 和 ObjectCreated -- 在對(duì)象被創(chuàng)建之前和之后引發(fā)
* ObjectDisposing -- 在對(duì)象被釋放之前引發(fā)
結(jié)論:
通常,在操作發(fā)生之前激發(fā)的事件用于取消操作(通過將事件參數(shù)的 Cancel 屬性設(shè)置為 true),或用于執(zhí)行參數(shù)驗(yàn)證或?qū)Σ糠謹(jǐn)?shù)據(jù)預(yù)處理。在操作之后引發(fā)的事件用于編寫自定義代碼以響應(yīng)給定的操作,或用于檢查操作的成功狀態(tài)。例如,可以檢查 Update、Insert 或 Delete 操作導(dǎo)致的 RowsAffected,或檢查 Exception 屬性以確定處理期間是否發(fā)生了異常。還可以設(shè)置事件參數(shù)的 ExceptionHandled 屬性以防止異常向上冒泡到控件或頁(yè)。
if (e.Exception != null)
...{
if (e.Exception.InnerException != null)
...{
Response.Write(string.Format("<script>alert('{0}')</script>", e.Exception.InnerException.Message));
}
else
...{
Response.Write(string.Format("<script>alert('{0}')</script>", e.Exception.InnerException.Message));
}
e.ExceptionHandled = true;
}
處理參數(shù):
如何處理各種數(shù)據(jù)控件事件以枚舉通過事件參數(shù)傳遞的參數(shù)集合?注意,與不需要插入數(shù)據(jù)庫(kù)的字段 相關(guān)聯(lián)的BoundField 的 InsertVisible 屬性應(yīng)該設(shè)置為 false,例如 OrderID 字段是基礎(chǔ)數(shù)據(jù)庫(kù)中的標(biāo)識(shí)列,不應(yīng)該將它傳遞給 Insert 操作(數(shù)據(jù)庫(kù)在插入操作發(fā)生時(shí)自動(dòng)遞增此值)。另請(qǐng)注意,如果OrderID 字段被標(biāo)記為 DataKeyNames 中的主鍵,那么此字段的原始值保留在數(shù)據(jù)綁定控件所傳遞的 Keys 字典中。用戶向輸入控件中輸入的值在 NewValues 字典中傳遞,不過標(biāo)記為 ReadOnly=false 的字段除外。非鍵字段的原始值也由數(shù)據(jù)綁定控件保留在一個(gè) OldValues 字典中,以用于傳遞給數(shù)據(jù)源。這些參數(shù)值由 SqlDataSource 按照 NewValues、Keys 和 OldValues 的順序追加到命令上,不過在 ConflictDetection 設(shè)置為 OverwriteChanges 時(shí),數(shù)據(jù)源默認(rèn)不追加 OldValues。有關(guān)數(shù)據(jù)源如何使用 OldValues 的更多信息,請(qǐng)參考QuickStart中的“使用沖突檢測(cè)”一節(jié)。
通過按照自己喜歡的順序?qū)㈧o態(tài) Parameter 對(duì)象添加到數(shù)據(jù)源操作的參數(shù)集合,可以更改 SqlDataSource 向命令追加參數(shù)的順序。SqlDataSource 自動(dòng)根據(jù)這些 Parameter 對(duì)象的順序?qū)?shù)據(jù)綁定控件傳遞的參數(shù)重新排序。這在數(shù)據(jù)源的 ProviderName 屬性設(shè)置為 System.Data.OleDb 時(shí)很有用,因?yàn)榇嗽O(shè)置不支持命名的參數(shù),所以向命令追加參數(shù)的順序必須與命令中的匿名參數(shù)占位符(“?”)的順序相匹配。當(dāng)使用命名的參數(shù)時(shí),參數(shù)的順序無關(guān)緊要??梢灾付?Parameter 對(duì)象的 Type 屬性,以便在執(zhí)行命令或方法之前,強(qiáng)制將數(shù)據(jù)綁定控件傳遞的值轉(zhuǎn)換為適當(dāng)?shù)臄?shù)據(jù)類型。同樣,可以設(shè)置 Parameter 的 Size 屬性,以指示 SqlDataSource 命令中的 DbParameter 的 Size (這是輸入/輸出、輸出和返回值參數(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>
<asp:Parameter Name="OrderDate" Type="DateTime" />
<asp:Parameter Name="ShipCountry" Type="String" />
<asp:Parameter Name="OrderID" Type="Int32" />
</UpdateParameters>
</asp:SqlDataSource>
參數(shù)名稱的默認(rèn)約定要求按照數(shù)據(jù)源 Select 操作所選擇的字段對(duì)新值進(jìn)行命名。通過指定 OldValuesParameterFormatString 屬性(例如“original_{0}”),可以對(duì)來自 Keys 或 OldValues 的參數(shù)重命名,以將它們與 NewValues 區(qū)別開來。通過處理相應(yīng)的事件以便在執(zhí)行數(shù)據(jù)源操作之前更改參數(shù)的值,您還可以自定義參數(shù)的名稱。例如,如果 SqlDataSource 的更新操作與某個(gè)存儲(chǔ)過程關(guān)聯(lián),該存儲(chǔ)過程采用的參數(shù)名稱與匹配默認(rèn)約定的名稱不同,可以在調(diào)用該存儲(chǔ)過程之前在 SqlDataSource Updating 事件中修改參數(shù)名稱。
void SqlDataSource1_Updating(Object sender, System.Web.UI.WebControls.SqlDataSourceCommandEventArgs e)
{
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"]);
}
ObjectDataSource 不依賴特定的參數(shù)順序,而只是查找具有匹配的參數(shù)名稱的方法。注意,為了解析方法重載,ObjectDataSource 沒有使用參數(shù)的 Type 或 Size。只對(duì)參數(shù)的名稱進(jìn)行匹配,因此如果業(yè)務(wù)對(duì)象上有兩個(gè)具有相同名稱和參數(shù)名稱但具有不同參數(shù)類型的方法,ObjectDataSource 將無法區(qū)分它們??梢栽谑录懈?ObjectDataSource 參數(shù)的名稱和值,類似于上面的 SqlDataSource 示例。但是,如果使用 DataObjectTypeName 指定要傳遞給 Update、Insert 和 Delete 操作的特定數(shù)據(jù)對(duì)象類型,您將不能修改參數(shù)名稱 -- 只能修改值。如果需要修改參數(shù)名稱,則不要使用 DataObjectTypeName,而只需在代碼中的數(shù)據(jù)源事件中手動(dòng)構(gòu)造相應(yīng)的數(shù)據(jù)對(duì)象。