本文英文原版與代碼下載:
http://www.asp.net/learn/dataaccess/tutorial64cs.aspx?tabid=63
導(dǎo)言:
在前面的教程,我們對(duì)數(shù)據(jù)訪問(wèn)層進(jìn)行擴(kuò)展以支持?jǐn)?shù)據(jù)庫(kù)事務(wù).數(shù)據(jù)庫(kù)事務(wù)確保一系列的操作要么都成功,要么都失敗。本文我們將注意力轉(zhuǎn)到創(chuàng)建一個(gè)批更新數(shù)據(jù)界面.
在本文,我們將創(chuàng)建一個(gè)GridView控件,里面的每一行記錄都可以進(jìn)行編輯(見(jiàn)圖1),因此我們沒(méi)有必要多添加一列來(lái)包含Edit, Update,和Cancel按鈕,而是在頁(yè)面包含2個(gè)“Update Products”按鈕,被點(diǎn)擊時(shí),遍歷所有的產(chǎn)品并對(duì)數(shù)據(jù)庫(kù)進(jìn)行更新.讓我們開(kāi)始吧.
圖1:GridView控件里的每一行記錄都可以編輯
注意:在第37章《Performing Batch Updates》里我們用一個(gè)DataList控件創(chuàng)建了一個(gè)批編輯界面, 那篇文章與本文的區(qū)別之一在于本文使用GridView控件且使用了事務(wù).
考察設(shè)置所有GridView Rows可編輯的步驟
就像在第16章《An Overview of Inserting, Updating, and Deleting》考察的那樣,GridView控件使用內(nèi)置的編輯功能編輯每一行記錄。在其內(nèi)部,GridView控件通過(guò)EditIndex屬性來(lái)判斷哪一行可編輯. 一旦GridView綁定到數(shù)據(jù)源之后,它就逐行檢查,看哪行的index值與EditIndex的值匹配,如果找到的話,該行就呈現(xiàn)為編輯界面.如果是綁定列(BoundFields),則呈現(xiàn)為一個(gè)TextBox,其Text值為對(duì)應(yīng)的BoundField的DataField屬性的值;如果是模板列(TemplateFields),那么呈現(xiàn)為EditItemTemplate而不是ItemTemplate.
我們知道當(dāng)某個(gè)用戶點(diǎn)擊某行的Edit按鈕時(shí),頁(yè)面產(chǎn)生回傳,將該行的index值為GridView控件的EditIndex屬性賦值,再重新綁定數(shù)據(jù).當(dāng)點(diǎn)擊某行的Cancel按鈕后產(chǎn)生頁(yè)面回傳,在重新綁定數(shù)據(jù)以前,將EditIndex屬性設(shè)置為-1.因?yàn)椋瑢?duì)GridView控件的rows而言,開(kāi)始時(shí)Index值為0,而將EditIndex設(shè)為-1的話就變成只讀模式了.
如果只對(duì)行進(jìn)行編輯,EditIndex屬性工作正常,但不支持批編輯。要對(duì)GridView實(shí)施批編輯的話,我們必須使每行都呈現(xiàn)為編輯界面.為此,最簡(jiǎn)單的方法是將要編輯的列,轉(zhuǎn)換為T(mén)emplateField,然后在ItemTemplate模板里創(chuàng)建編輯界面.在接下來(lái)的幾步,我們將創(chuàng)建一個(gè)完整的可批編輯的GridView,在第一步,我們將創(chuàng)建一個(gè)GridView及其ObjectDataSource,并將BoundFields和CheckBoxField轉(zhuǎn)換為T(mén)emplateFields。在第二步和第三步,我們將編輯界面從ItemTemplates模板轉(zhuǎn)移到EditItemTemplates.
第一步:展示Product信息
首先,我們先創(chuàng)建一個(gè)顯示產(chǎn)品信息的GridView.打開(kāi)BatchData文件夾里的頁(yè)面BatchUpdate.aspx,從工具箱拖一個(gè)GridView控件到頁(yè)面,設(shè)ID值為ProductsGrid,從其智能標(biāo)簽里綁定到一個(gè)名為ProductsDataSource的ObjectDataSource,設(shè)其調(diào)用ProductsBLL class類的GetProducts方法.
圖2:設(shè)置ObjectDataSourc調(diào)用ProductsBLL Class類
圖3: 使用GetProducts方法獲取產(chǎn)品信息
像GridView一樣,該ObjectDataSource調(diào)用的方法也只能對(duì)每行記錄起作用。為了批更新記錄,我們必須在ASP.NET頁(yè)面的后臺(tái)代碼類里多寫(xiě)些代碼,批處理數(shù)據(jù)并傳遞給BLL.因此,在ObjectDataSource的UPDATE, INSERT,和DELETE標(biāo)簽里選“(None)”. 點(diǎn)Finish完成設(shè)置.
圖4:在UPDATE, INSERT,和DELETE標(biāo)簽里選“(None)”
完成設(shè)置后,ObjectDataSource控件的聲明代碼看起來(lái)和下面的差不多:
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
完成設(shè)置后,Visual Studio會(huì)向GridView控件添加BoundFields以及一個(gè) CheckBoxField.就本文而言,我們只允許用戶查看和編輯產(chǎn)品的名稱、類別、價(jià)格、以及discontinued狀態(tài).將ProductName, CategoryName, UnitPrice和 Discontinued以外的列全部刪除,并分別將頭3個(gè)列的HeaderText屬性設(shè)置為“Product”, “Category”,“Price”。最后,啟用GridView的分頁(yè)、排序功能.
此時(shí),GridView控件含有3個(gè)BoundFields(ProductName,CategoryName,和UnitPrice)以及一個(gè)CheckBoxField (Discontinued).我們希望將這4個(gè)列轉(zhuǎn)換為T(mén)emplateFields,并將編輯界面從TemplateField的EditItemTemplate模板轉(zhuǎn)移到ItemTemplate模板.
注意:我們?cè)诘?0章《Customizing the Data Modification Interface》里探討了如何創(chuàng)建并定制TemplateFields.我們將BoundFields和CheckBoxField轉(zhuǎn)換成TemplateFields,然后再在ItemTemplates模板里定制其編輯界面。如果有什么不清楚的,可參考前面的文章.
從GridView的智能標(biāo)簽里,點(diǎn)“編輯列”,這將打開(kāi)Fields對(duì)話框,然后選中每一列,點(diǎn)擊“Convert this field into a TemplateField”。
圖5:將現(xiàn)有的BoundFields和CheckBoxField轉(zhuǎn)換為T(mén)emplateField
現(xiàn)在每一列都是TemplateField,我們將把編輯界面從EditItemTemplates模板轉(zhuǎn)移到ItemTemplates模板.
第2步:創(chuàng)建ProductName, UnitPrice,和Discontinued列的編輯界面
創(chuàng)建ProductName, UnitPrice,和Discontinued這3列的編輯界面是比較簡(jiǎn)單的,因?yàn)樗鼈兌荚赥emplateField的EditItemTemplate模板里定義好了的;而創(chuàng)建CategoryName的編輯界面比較麻煩,因?yàn)槲覀冃枰獎(jiǎng)?chuàng)建一個(gè)DropDownList控件來(lái)顯示可用的categories,我們將在第3步實(shí)現(xiàn).
我們首先創(chuàng)建ProductName的編輯界面。在GridView控件的智能標(biāo)簽里點(diǎn)“編輯模板”,再點(diǎn)ProductName TemplateField的EditItemTemplate項(xiàng).選中其中的TextBox,將其復(fù)制、粘貼到ProductName TemplateField的ItemTemplate模板.將該TextBox的ID屬性設(shè)置為ProductName.
然后,在ItemTemplate模板里添加一個(gè)RequiredFieldValidator控件,以確保用戶輸入的產(chǎn)品name不為空.將其ControlToValidate屬性設(shè)置為“ProductName”;ErrorMessage屬性為“You must provide the product's name.”;Text屬性為“*”.添加完后,屏幕看起來(lái)應(yīng)該像圖6那樣:
圖6:ProductName TemplateField現(xiàn)在包含一個(gè)TextBox控件和一個(gè) RequiredFieldValidator控件
對(duì)UnitPrice編輯界面而言,先從EditItemTemplate模板里將TextBox拷貝到ItemTemplate模板.然后,在TextBox前面放置一個(gè)“$”符合,將其ID屬性設(shè)置為“UnitPrice”;Columns屬性設(shè)置為“8”.
然后再添加一個(gè)CompareValidator控件,確保用戶輸入的是大于或等于$0.00的貨幣值.設(shè)其ControlToValidate屬性為“UnitPrice”;ErrorMessage 屬性為“You must enter a valid currency value. Please omit any currency symbols.”;Text屬性為“*”;Type屬性為Currency;Operator屬性為GreaterThanEqual;ValueToCompare屬性為“0”.
圖7:添加一個(gè)CompareValidator控件以確保用戶輸入的是非負(fù)的貨幣值
對(duì)Discontinued TemplateField而言,直接使用已經(jīng)在ItemTemplate模板里定義好了的CheckBox,只需要設(shè)其ID為“Discontinued”,Enabled屬性為true.
第三步:創(chuàng)建CategoryName的編輯界面
CategoryName TemplateField的EditItemTemplate模板里的編輯界面里包含一個(gè)TextBox,其用來(lái)顯示CategoryName列的值,我們要將其替換為一個(gè)DropDownList控件以顯示categories.
注意:在第20章《Customizing the Data Modification Interface》里我們?cè)敿?xì)地探討了如何用DropDownList控件來(lái)替換TextBox控件。在此我們將過(guò)程一略而過(guò),具體創(chuàng)建和設(shè)置DropDownList控件的細(xì)節(jié)可參考第20章.
從工具箱里拖一個(gè)DropDownList控件到CategoryNameTemplateField的ItemTemplate模板, 設(shè)其ID為Categories.通常情況下,我們會(huì)通過(guò)其智能標(biāo)簽來(lái)定義DropDownLists的數(shù)據(jù)源,來(lái)創(chuàng)建一個(gè)新的ObjectDataSource.然而,這將在ItemTemplate模板里新添一個(gè)ObjectDataSource,后果是每一個(gè)GridView row都會(huì)創(chuàng)建一個(gè)ObjectDataSource實(shí)例.因此,我們?cè)贕ridView的TemplateFields外創(chuàng)建ObjectDataSource.結(jié)束模板編輯,從工具箱拖一個(gè)ObjectDataSource到頁(yè)面,放置在名為ProductsDataSource的ObjectDataSource控件下面。將該新O用GetCategories Method bjectDataSource命名為CategoriesDataSource,設(shè)其使用CategoriesBLL class類的GetCategories方法.
圖8:設(shè)置該ObjectDataSource使用CategoriesBLL類
圖9:從GetCategories方法獲取數(shù)據(jù)
因?yàn)樵揙bjectDataSource僅僅是用來(lái)檢索數(shù)據(jù),在UPDATE 和 DELETE標(biāo)簽里選 “(None)”. 點(diǎn)Finish完成設(shè)置.
圖10:在UPDATE和DELETE標(biāo)簽里選“(None)”
完成設(shè)置后,CategoriesDataSource的聲明代碼看起來(lái)根下面的差不多:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
設(shè)置好后,返回CategoryName TemplateField的ItemTemplate模板,在DropDownList的智能標(biāo)簽里點(diǎn)“Choose Data Source”,在數(shù)據(jù)源設(shè)置向?qū)Ю铮诘谝粋€(gè)下拉列表里選CategoriesDataSource;再下面的2個(gè)下拉列表里分別選CategoryName和CategoryID.
圖11:將DropDownList控件綁定到CategoriesDataSource
此時(shí),DropDownList控件雖然列出了所有的categories,但對(duì)綁定到GridViewrow里的產(chǎn)品而言,其并沒(méi)有自動(dòng)的選擇產(chǎn)品對(duì)應(yīng)的category.為此,我們將DropDownList的SelectedValue值設(shè)置為產(chǎn)品的CategoryID值。在DropDownList的智能標(biāo)簽里點(diǎn)“Edit DataBindings”,并將SelectedValue屬性賦值為CategoryID ,如圖12:
圖12:將產(chǎn)品的CategoryID值綁定到DropDownList的SelectedValue屬性
還有最后一個(gè)問(wèn)題,如果產(chǎn)品的CategoryID為空的話,對(duì)SelectedValue的數(shù)據(jù)綁定將會(huì)拋出異常. 因?yàn)镈ropDownList只列出了那些指定了CategoryID值的產(chǎn)品,但不會(huì)列出那些CategoryID值為NULL的產(chǎn)品.怎樣解決呢?將DropDownList的AppendDataBoundIt屬性設(shè)為rue,并向DropDownList新添加一個(gè)item,忽略其Value屬性就像下面的聲明代碼那樣:
<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource" DataTextField="CategoryName"
DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
<asp:ListItem Value="">-- Select One --</asp:ListItem>
</asp:DropDownList>
我們注意到<asp:ListItem Value=""> “-- Select One --”里,將Value屬性設(shè)置為一個(gè)空字符串.為什么要新添該item來(lái)處理值為NULL的情況?為什么要將Value屬性設(shè)置為一個(gè)空字符串呢?這些疑問(wèn)可參考前面第20章《Customizing the Data Modification Interface》
注意:這里有一個(gè)關(guān)乎性能的潛在問(wèn)題要提一下。因?yàn)槊啃杏涗浂及粋€(gè)DropDownList,其數(shù)據(jù)源為CategoriesDataSource.每次登錄頁(yè)面時(shí),都會(huì)調(diào)用CategoriesBLL class類的GetCategories方法N次,這里N為GridView控件里行的數(shù)目.對(duì)GetCategories的N次調(diào)用就會(huì)導(dǎo)致對(duì)數(shù)據(jù)庫(kù)的N次查詢.我們可以對(duì)返回結(jié)果進(jìn)行緩存以減輕對(duì)數(shù)據(jù)庫(kù)造成的影響;至于方式嘛,可以運(yùn)用per-request caching策略,也可以在緩存層Caching Layer里使用SQL高速緩存依賴性(SQL caching dependency)或基于短時(shí)間緩存周期(a very short time-based expiry)的策略。對(duì)per-request caching策略的更多信息可參考文章《HttpContext.Items – a Per-Request Cache Store》(
http://aspnet.4guysfromrolla.com/articles/060904-1.aspx
)
第四步:完善編輯界面
在瀏覽器里查看該頁(yè)面,就像圖13所示,每行都使用ItemTemplate模板,以包含其編輯頁(yè)面。
圖13:每個(gè)GridView Row都是可編輯的
不過(guò)仍有一些問(wèn)題。首先,UnitPrice值為四個(gè)小數(shù)點(diǎn),為此,返回UnitPrice TemplateField的ItemTemplate模板, 在TextBox的智能標(biāo)簽里點(diǎn)“Edit DataBindings”,然后,將Text屬性格式指定為number.
然后,將Discontinued列里的checkbox控件居中(而不是居左),在GridView的智能標(biāo)簽里點(diǎn)“編輯列”,選取左邊方框里的Discontinued,再在右邊方框里的ItemStyle里將HorizontalAlign屬性設(shè)置為Center,如圖15所示:
圖15:將Discontinued列里的CheckBox居左
接下來(lái)在頁(yè)面上添加一個(gè)ValidationSummar控件,將其ShowMessageBox屬性設(shè)置為true;ShowSummary屬性設(shè)置為false. 同時(shí)再添加一個(gè)Button Web控件,用來(lái)更新用戶所做的更該。特別的,添加2個(gè),一個(gè)在GridView控件上面,一個(gè)在下面,將它們的Text屬性設(shè)置為“Update Products”.由于我們已經(jīng)在TemplateFields模板定義了編輯界面,那么EditItemTemplates模板就顯得多余了,將其刪除.
完成上述修改后,你的頁(yè)面聲明代碼看起來(lái)應(yīng)該和下面的差不多:
<p>
<asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<ItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>'></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName"
ErrorMessage="You must provide the product's name."
runat="server">*</asp:RequiredFieldValidator>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Category"
SortExpression="CategoryName">
<ItemTemplate>
<asp:DropDownList ID="Categories" runat="server"
AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Bind("CategoryID") %>'>
<asp:ListItem>-- Select One --</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price"
SortExpression="UnitPrice">
<ItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="You must enter a valid currency value.
Please omit any currency symbols."
Operator="GreaterThanEqual" Type="Currency"
ValueToCompare="0">*</asp:CompareValidator>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="Discontinued" runat="server"
Checked='<%# Bind("Discontinued") %>' />
</ItemTemplate>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>
</Columns>
</asp:GridView>
</p>
<p>
<asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
<asp:ValidationSummary ID="ValidationSummary1" runat="server"
ShowMessageBox="True" ShowSummary="False" />
</p>
當(dāng)添加Button Web控件并對(duì)相關(guān)格式進(jìn)行修改后,頁(yè)面如下圖所示:
圖16:頁(yè)面現(xiàn)在包含了2個(gè)“Update Products”按鈕
第五步:更新產(chǎn)品
當(dāng)用戶登錄該頁(yè)面進(jìn)行修改時(shí)并點(diǎn)擊“Update Products”按鈕時(shí),我們需要將用戶輸入的值保存為一個(gè)ProductsDataTable instance實(shí)例;再將該實(shí)例傳遞給一個(gè)BLL method方法,進(jìn)而將該實(shí)例傳遞給DAL層的UpdateWithTransaction method方法。該方法是在前面的文章里創(chuàng)建的,確保對(duì)批處理進(jìn)行原子操作.
在BatchUpdate.aspx.cs文件里創(chuàng)建一個(gè)名為BatchUpdate的方法,代碼如下:
private void BatchUpdate()
{
// Enumerate the GridView's Rows collection and create a ProductRow
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
foreach (GridViewRow gvRow in ProductsGrid.Rows)
{
// Find the ProductsRow instance in products that maps to gvRow
int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
Northwind.ProductsRow product = products.FindByProductID(productID);
if (product != null)
{
// Programmatically access the form field elements in the
// current GridViewRow
TextBox productName = (TextBox)gvRow.FindControl("ProductName");
DropDownList categories =
(DropDownList)gvRow.FindControl("Categories");
TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
CheckBox discontinued =
(CheckBox)gvRow.FindControl("Discontinued");
// Assign the user-entered values to the current ProductRow
product.ProductName = productName.Text.Trim();
if (categories.SelectedIndex == 0)
product.SetCategoryIDNull();
else
product.CategoryID = Convert.ToInt32(categories.SelectedValue);
if (unitPrice.Text.Trim().Length == 0)
product.SetUnitPriceNull();
else
product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
product.Discontinued = discontinued.Checked;
}
}
// Now have the BLL update the products data using a transaction
productsAPI.UpdateWithTransaction(products);
}
該方法調(diào)用BLL層的GetProducts method方法,通過(guò)一個(gè)ProductsDataTable來(lái)獲取所有的產(chǎn)品.然后遍歷GridView控件的Rows collection集,該Rows collection集包含了GridView里每行所對(duì)應(yīng)的GridViewRow instance實(shí)例。由于GridView里每頁(yè)最多顯示了10行,所以GridView控件的Rows collection集包含的條碼最多不超過(guò)10條.
每行記錄的ProductID來(lái)源于DataKeys collection集,并從ProductsDataTable里選出對(duì)應(yīng)的ProductsRow.這4個(gè)TemplateField input控件的值賦值給ProductsRow instance實(shí)例的屬性。當(dāng)對(duì)ProductsDataTable更新完成后,又轉(zhuǎn)到BLL業(yè)務(wù)邏輯層的UpdateWithTransaction method方法,就像我們?cè)谇懊娴慕坛炭吹降囊粯樱摲椒▋H僅調(diào)用DAL數(shù)據(jù)訪問(wèn)層的UpdateWithTransaction方法.
本文使用的批更新策略是:將ProductsDataTable里對(duì)應(yīng)于GridView里每行記錄的所有row進(jìn)行更新,不管用戶有沒(méi)有改動(dòng)過(guò)產(chǎn)品信息.這種盲目的更改雖然執(zhí)行起來(lái)沒(méi)什么問(wèn)題,但將會(huì)導(dǎo)致database table里出現(xiàn)多余的記錄.在前面的第37章《Performing Batch Updates》里,我們考察里DataList控件的批更新界面,在那篇文章里我們使用餓代碼只更新那些確實(shí)被用戶改動(dòng)過(guò)的記錄.如果愿意的話,你可以使用37章的方法.
注意:當(dāng)通過(guò)GridView的智能標(biāo)簽來(lái)綁定數(shù)據(jù)源時(shí),Visual Studio會(huì)自動(dòng)的將數(shù)據(jù)源的主鍵值指定為GridView的DataKeyNames屬性.如果你沒(méi)有通過(guò)GridView的智能標(biāo)簽來(lái)綁定ObjectDataSource的話,我們需要手工設(shè)置GridView控件DataKeyNames屬性為“ProductID”, 以便通過(guò)DataKeys collection集來(lái)訪問(wèn)ProductID值.
BatchUpdate方法里的代碼和BLL業(yè)務(wù)邏輯層里的UpdateProduct methods方法的代碼很相似,主要的區(qū)別在于UpdateProduct methods方法僅僅獲取一個(gè)單一的ProductRow instance實(shí)例.UpdateProducts methods方法里對(duì)ProductRow的屬性賦值的代碼與BatchUpdate方法里foreach循環(huán)里的代碼是一模一樣的.
最后,當(dāng)點(diǎn)擊任意一個(gè)“Update Products”按鈕時(shí),將調(diào)用BatchUpdate方法,為這2個(gè)按鈕的Click events事件創(chuàng)建事件處理器,在里面添加如下的代碼:
BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message",
"alert('The products have been updated.');", true);
以上代碼首先調(diào)用BatchUpdate()方法;再使用ClientScript property屬性來(lái)注入JavaScript,以顯示一個(gè)messagebox,提示“The products have been updated.”
花幾分鐘測(cè)試代碼.在瀏覽器的登錄BatchUpdate.aspx頁(yè)面,編輯幾行記錄,點(diǎn)任意一個(gè)“Update Products”按鈕。假定輸入無(wú)誤,你會(huì)看到一個(gè)消息框顯示“The products have been updated.”為了測(cè)試原子操作,你可以任意添加一個(gè)CHECK約束,
比如不接受UnitPrice的值為“1234.56”。然后再登錄BatchUpdate.aspx頁(yè)面,編輯幾行記錄,確保設(shè)置其中的一條記錄的UnitPrice值為“1234.56”. 當(dāng)點(diǎn)“Update Products”按鈕時(shí),將會(huì)出錯(cuò)。結(jié)果是所有的操作回滾,回到原來(lái)的值.
另一種可供選擇的BatchUpdate方法
上面我們探討的BatchUpdate方法從BLL業(yè)務(wù)邏輯層的GetProducts方法獲取所有的產(chǎn)品.
如果GridView沒(méi)有啟用分頁(yè)的話,一切都很完美.如果啟用了分頁(yè)了呢?比如可能總共有幾百、幾千、幾萬(wàn)條產(chǎn)品記錄,而GridView里每頁(yè)只顯示了10條記錄。在這種情況下,該方法獲取了所有的記錄,但只更新其中的10條記錄,實(shí)在是難稱完美.
面對(duì)這種情況,可以考慮使用下面的BatchUpdateAlternate代替:
private void BatchUpdateAlternate()
{
// Enumerate the GridView's Rows collection and create a ProductRow
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
foreach (GridViewRow gvRow in ProductsGrid.Rows)
{
// Create a new ProductRow instance
int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
Northwind.ProductsDataTable currentProductDataTable =
productsAPI.GetProductByProductID(productID);
if (currentProductDataTable.Rows.Count > 0)
{
Northwind.ProductsRow product = currentProductDataTable[0];
// Programmatically access the form field elements in the
// current GridViewRow
TextBox productName = (TextBox)gvRow.FindControl("ProductName");
DropDownList categories =
(DropDownList)gvRow.FindControl("Categories");
TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
CheckBox discontinued =
(CheckBox)gvRow.FindControl("Discontinued");
// Assign the user-entered values to the current ProductRow
product.ProductName = productName.Text.Trim();
if (categories.SelectedIndex == 0)
product.SetCategoryIDNull();
else
product.CategoryID = Convert.ToInt32(categories.SelectedValue);
if (unitPrice.Text.Trim().Length == 0)
product.SetUnitPriceNull();
else
product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
product.Discontinued = discontinued.Checked;
// Import the ProductRow into the products DataTable
products.ImportRow(product);
}
}
// Now have the BLL update the products data using a transaction
productsAPI.UpdateProductsWithTransaction(products);
}
該方法首先創(chuàng)建一個(gè)名為products的空白的ProductsDataTable,再通過(guò)BLL業(yè)務(wù)邏輯層的GetProductByProductID(productID)方法來(lái)獲取具體的產(chǎn)品信息.獲取的ProductsRow instance實(shí)例更新其屬性,就像BatchUpdate()做的那樣。更新完后,通過(guò)ImportRow(DataRow)method方法將row導(dǎo)入名為products的ProductsDataTable.
foreach循環(huán)完成后, products將包含那些對(duì)應(yīng)于GridView里每行記錄的ProductsRowinstance實(shí)例,由于這些實(shí)例是添加(而不是更新)到products,如果我們盲目的傳遞給UpdateWithTransaction method方法的話,ProductsTableAdatper會(huì)將每條記錄插入數(shù)據(jù)庫(kù).在此,我們必須聲明只對(duì)這些行進(jìn)行更新(而不是添加).
為此,我們需要在業(yè)務(wù)邏輯層里添加一個(gè)名為UpdateProductsWithTransaction的方法來(lái)達(dá)到上述目的。該方法,就像下面代碼顯示的那樣,將ProductsDataTable里的每一個(gè)ProductsRow instances實(shí)例的RowState設(shè)置為Modified,然后將該P(yáng)roductsDataTable傳遞給DAL數(shù)據(jù)訪問(wèn)層的UpdateWithTransaction method方法.
public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
// Mark each product as Modified
products.AcceptChanges();
foreach (Northwind.ProductsRow product in products)
product.SetModified();
// Update the data via a transaction
return UpdateWithTransaction(products);
}
總結(jié):
GridView控件內(nèi)置的編輯功能只能對(duì)每行進(jìn)行編輯,對(duì)批編輯無(wú)能為力.就像本文探討的那樣,要?jiǎng)?chuàng)建一個(gè)批處理界面我們要多做一些工作。為此,我們需要將GridView里的列轉(zhuǎn)換為T(mén)emplateFields,并在ItemTemplates模板里定義編輯界面,另外要在頁(yè)面添加“Update All”按鈕,該按鈕與GridView彼此分開(kāi).該按鈕的Click event事件必須要確保遍歷GridView的Rows collection集、在一個(gè)ProductsDataTable里存儲(chǔ)改動(dòng)信息,然后再傳遞給相應(yīng)的BLL業(yè)務(wù)邏輯層的方法.
下一篇,我們將考察如何創(chuàng)建一個(gè)批刪除的界面,具體來(lái)說(shuō),每個(gè)GridView row都會(huì)包含一個(gè)checkbox。另外, 我們將用一個(gè)“Delete Selected Rows”按鈕來(lái)替換“Update All”按鈕.
祝編程快樂(lè)!
作者簡(jiǎn)介:
Scott Mitchell,著有六本ASP/ASP.NET方面的書(shū),是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。Scott是個(gè)獨(dú)立的技術(shù)咨詢顧問(wèn),培訓(xùn)師,作家,最近完成了將由Sams出版社出版的新作,24小時(shí)內(nèi)精通ASP.NET 2.0。他的聯(lián)系電郵為 mitchell@4guysfromrolla.com ,也可以通過(guò)他的博 http://ScottOnWriting.NET 與他聯(lián)系
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
