導言
在DataList里編輯和刪除數(shù)據(jù)概述里,我們創(chuàng)建了一個提供簡單編輯和刪除功能的DataList。雖然功能上已經(jīng)完整了,但是對用戶來說是不友好的。因為所有在編輯和刪除過程中產(chǎn)生的異常都是未處理的。比如,遺漏了輸入product的name,或者編輯product時在price里輸入“Very affordable!”,都會拋出異常。而由于在代碼里未捕捉這些異常,頁面會顯示ASP.NET運行時的詳細錯誤信息。
如我們在在ASP.NET頁面中處理BLL/DAL層的異常里看到的,如果BLL或DAL里發(fā)生異常,詳細的信息會返回到ObjectDataSource,然后再到GridView。我們已經(jīng)學習了如何優(yōu)雅的處理這些異常:為ObjectDataSource或GridView創(chuàng)建Updated或RowUpdated事件處理,檢查異常,然后指明異常被處理。
然而在使用DataList時,我們并沒有通過ObjectDataSource來更新和刪除數(shù)據(jù)。我們是直接通過BLL來實現(xiàn)的。為了檢測到 BLL或DAL的異常,我們需要在ASP.NET頁里寫異常處理代碼。本章我們將學習在使用DataList編輯時如何巧妙的處理異常。
注意:在DataList里編輯和刪除數(shù)據(jù)概述里,我們討論了幾種不同的編輯和刪除數(shù)據(jù)的方法,其中一些會涉及到使用ObjectDataSource來編輯和刪除。如果你用這些技術的話,你可以直接通過ObjectDataSource的Updated或Deleted 事件處理中處理這些異常。
第一步: 創(chuàng)建一個可編輯的DataList
首先創(chuàng)建一個可編輯的DataList。打開EditDeleteDataList文件夾下的ErrorHandling.aspx頁,添加一個ID為Products的DataList和一個名為ProductsDataSource的ObjectDataSouce。在SELECT標簽下選擇ProductsBLL類的GetProducts()方法。在INSERT,UPDATE和DELETE標簽里選擇None.
圖 1: 配置ObjectDataSource
完成ObjectDataSouce后,Visual Studio會自動創(chuàng)建一個ItemTemplate。用顯示每個product的name和price并包含一個Edit button的ItemTemplate代替它,然后創(chuàng)建一個用TextBox顯示name和price,并包含一個Update button和Cancel button的EditItemTemplate。最后將DataList的RepeatColumns屬性設為2。
做完這些后,你的聲明代碼應該和下面的差不多。檢查并確保Edit,Cancel和Update button的CommandName屬性,分別被設為“Edit”, “Cancel”, 和“Update”。
asp:DataList ID="Products" runat="server" DataKeyField="ProductID" DataSourceID="ProductsDataSource" RepeatColumns="2"> ItemTemplate> h5> asp:Label runat="server" ID="ProductNameLabel" Text='%# Eval("ProductName") %>' /> /h5> Price: asp:Label runat="server" ID="Label1" Text='%# Eval("UnitPrice", "{0:C}") %>' /> br /> asp:Button runat="server" id="EditProduct" CommandName="Edit" Text="Edit" /> br /> br /> /ItemTemplate> EditItemTemplate> Product name: asp:TextBox ID="ProductName" runat="server" Text='%# Eval("ProductName") %>' /> br /> Price: asp:TextBox ID="UnitPrice" runat="server" Text='%# Eval("UnitPrice", "{0:C}") %>' /> br /> br /> asp:Button ID="UpdateProduct" runat="server" CommandName="Update" Text="Update" /> asp:Button ID="CancelUpdate" runat="server" CommandName="Cancel" Text="Cancel" /> /EditItemTemplate> /asp:DataList> asp:ObjectDataSource ID="ProductsDataSource" runat="server" SelectMethod="GetProducts" TypeName="ProductsBLL" OldValuesParameterFormatString="original_{0}"> /asp:ObjectDataSource>
注意:本章里DataList的view state必須開啟。瀏覽一下頁面,見圖2。
圖 2: 每個Product 都包含一個Edit Button
現(xiàn)在Edit button只是引起一個postback —還不能將product變成可編輯的。為了實現(xiàn)編輯功能,我們需要為EditCommand,CancelCommand和UpdateCommand創(chuàng)建事件處理。EditCommand和CancelCommand事件僅僅只需要更新DataList的EditItemIndex屬性,并重新綁定數(shù)據(jù)到DataList。
protected void Products_EditCommand(object source, DataListCommandEventArgs e) { // Set the DataList's EditItemIndex property to the // index of the DataListItem that was clicked Products.EditItemIndex = e.Item.ItemIndex; // Rebind the data to the DataList Products.DataBind(); } protected void Products_CancelCommand(object source, DataListCommandEventArgs e) { // Set the DataList's EditItemIndex property to -1 Products.EditItemIndex = -1; // Rebind the data to the DataList Products.DataBind(); }
UpdateCommand事件處理稍微麻煩一點。它需要從DataKey集合里讀取被編輯的product的ProductID,和EditItemTemplate里的TextBox里的product的name和price,然后調(diào)用ProductsBLL類的UpdateProduct方法,最后返回到DataList編輯前的狀態(tài)。
我們在這里使用 在DataList里編輯和刪除數(shù)據(jù)概述 里的UpdateCommand事件處理代碼。
protected void Products_UpdateCommand(object source, DataListCommandEventArgs e) { // Read in the ProductID from the DataKeys collection int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]); // Read in the product name and price values TextBox productName = (TextBox)e.Item.FindControl("ProductName"); TextBox unitPrice = (TextBox)e.Item.FindControl("UnitPrice"); string productNameValue = null; if (productName.Text.Trim().Length > 0) productNameValue = productName.Text.Trim(); decimal? unitPriceValue = null; if (unitPrice.Text.Trim().Length > 0) unitPriceValue = Decimal.Parse(unitPrice.Text.Trim(), System.Globalization.NumberStyles.Currency); // Call the ProductsBLL's UpdateProduct method... ProductsBLL productsAPI = new ProductsBLL(); productsAPI.UpdateProduct(productNameValue, unitPriceValue, productID); // Revert the DataList back to its pre-editing state Products.EditItemIndex = -1; Products.DataBind(); }
在有非法輸入的時候— 可能是不正確的price格式,比如“-$5.00”,或者忽略了product的name— 就會引起異常。由于UpdateCommand事件處理還沒有處理異常,頁面會出現(xiàn)ASP.NET運行時錯誤。見圖3。
圖 3: 未處理異常發(fā)生時,用戶會看到這樣的錯誤頁面
第二步: 在UpdateCommand Event Handler里處理異常
更新流程中,異??赡馨l(fā)生在UpdateCommand事件處理,或BLL或DAL里。比如,如果用戶輸入了一個“太貴”的價格,UpdateCommand 事件處理里的Decimal.Parse 會拋出FormatException 異常。如果用戶忽略了product的name或者price是一個負數(shù),DAL會拋出異常。
當異常發(fā)生時,我們希望顯示自己定義的信息。添加一個ID為ExceptionDetails的Label控件到頁面上,通過設置CssClass屬性為Warning CSS類來將Text設置為紅色,特大,加粗的意大利字體。這個類在Styles.css文件里定義。
異常發(fā)生時,我們只希望這個 Label顯示一次。也就是說,在后面postback的時候,Label的警告信息需要隱藏起來。這個可以通過清除Label的Text屬性或者將Visible屬性設為False(在Page_Load里)(如我們在在ASP.NET頁面中處理BLL/DAL層的異常 里做的那樣)或者禁用Label的view state來實現(xiàn)。我們這里用后一種方法。
asp:Label ID="ExceptionDetails" EnableViewState="False" CssClass="Warning" runat="server" />
異常發(fā)生時,我們將異常的信息顯示在Label的Text屬性上。由于view state被禁用了,后面再postback的話,Text屬性會自動的丟失,回到缺省值(空字符串),這樣就隱藏了警告信息。
異常發(fā)生時將信息顯示在頁面上,我們需要在UpdateCommand事件處理里添加Try....Catch塊。Try的那部分包含可能拋出異常的代碼,Catch部分包含當出現(xiàn)異常時需要執(zhí)行的代碼。更多的Try..Catch塊信息參考Exception Handling Fundamentals 。
protected void Products_UpdateCommand(object source, DataListCommandEventArgs e) { // Handle any exceptions raised during the editing process try { // Read in the ProductID from the DataKeys collection int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]); ... Some code omitted for brevity ... } catch (Exception ex) { // TODO: Display information about the exception in ExceptionDetails } }
無論Try塊里拋出何種類型的異常,Catch塊的代碼都會執(zhí)行。拋出異常的類型—DbException, NoNullAllowedException, ArgumentException等 — 取決于第一個錯誤。如果是數(shù)據(jù)庫級別的問題,會拋出DbException 。如果是UnitPrice, UnitsInStock, UnitsOnOrder, 或ReorderLevel 字段有非法值,會拋出ArgumentException (我們在ProductsDataTable里已經(jīng)添加過驗證字段值的代碼,見創(chuàng)建一個業(yè)務邏輯層 )
我們可以根據(jù)捕捉到的異常的類型來為用戶提供更好的幫助。下面的代碼— 和在ASP.NET頁面中處理BLL/DAL層的異常 中基本上一樣— 提供了這個功能:
private void DisplayExceptionDetails(Exception ex) { // Display a user-friendly message ExceptionDetails.Text = "There was a problem updating the product. "; if (ex is System.Data.Common.DbException) ExceptionDetails.Text += "Our database is currently experiencing problems. Please try again later."; else if (ex is NoNullAllowedException) ExceptionDetails.Text += "There are one or more required fields that are missing."; else if (ex is ArgumentException) { string paramName = ((ArgumentException)ex).ParamName; ExceptionDetails.Text += string.Concat("The ", paramName, " value is illegal."); } else if (ex is ApplicationException) ExceptionDetails.Text += ex.Message; }
最后僅僅只需要調(diào)用DisplayExceptionDetails方法。
完成Try..Catch后,用戶會看到更有用的錯誤信息,如圖4,5所示。注意在異常出現(xiàn)時,DataList仍然保持在可編輯的模式下。這是因為異常發(fā)生時,控制流程馬上轉到Catch塊里,忽略了將DataList轉到編輯前狀態(tài)的代碼。
圖 4: 用戶忽略了必填字段時的錯誤信息
圖 5: 輸入非法價格時的錯誤信息
總結
GridView和ObjectDataSource提供了在更新和刪除過程中包含異常信息的事件處理和指明異常是否被處理的屬性。而直接使用BLL的DataList沒有這些特性,我們需要親自處理這些異常。
本章我們學習了如何在DataList的更新過程中在UpdateCommand里添加Try...Catch塊來處理異常。如果有任何異常發(fā)生,Catch塊里的代碼會執(zhí)行,將錯誤信息顯示在ExceptionDetails Label上。
現(xiàn)在,DataList并沒有在第一時間阻止異常的發(fā)生。盡管我們知道一個負的price值會產(chǎn)生異常,我們也沒有添加阻止用戶輸入這樣的值的功能。在后面的幾章里,我們將學習如何通過在EditItemTemplate里添加驗證控件來驗證用戶的輸入從而減少這些異常的發(fā)生。
祝編程愉快!
作者簡介
本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來一直應用 微軟Web技術。大家可以點擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對大家的學習ASP.NET有所幫助。