由 斯科特·米切爾
在本教學課程中,我們將瞭解如何在可編輯的 DataList 更新工作流程期間處理引發的例外狀況。
簡介
在 DataList 教學課程中編輯和刪除資料的概觀中,我們建立了提供簡單編輯和刪除功能的 DataList 。 雖然功能完全正常,但很難方便使用者使用,因為編輯或刪除程式期間發生的任何錯誤都會導致未處理的例外狀況。 例如,省略產品名稱,或在編輯產品時輸入價格值非常實惠!,擲回例外狀況。 由於此例外狀況未在程式碼中攔截,因此會升至 ASP.NET 運行時間,然後在網頁中顯示例外狀況的詳細數據。
如我們在處理 ASP.NET Page 教學課程中的 BLL 和 DAL 層級例外狀況中所見,如果例外狀況是從商業規則或數據存取層的深度引發,則會將例外狀況詳細數據傳回至 ObjectDataSource,然後傳回至 GridView。 我們已瞭解如何透過建立 Updated ObjectDataSource 或 RowUpdated GridView 的事件處理程式、檢查例外狀況,然後指出已處理例外狀況,以正常處理這些例外狀況。
不過,我們的 DataList 教學課程並未使用 ObjectDataSource 來更新和刪除數據。 相反地,我們正直接針對 BLL 工作。 為了偵測源自 BLL 或 DAL 的例外狀況,我們需要在 ASP.NET 頁面的程式代碼後置內實作例外狀況處理程式代碼。 在本教學課程中,我們將瞭解如何更巧妙地處理在可編輯的 DataList 更新工作流程期間引發的例外狀況。
備註
在 DataList 教學課程中的編輯和刪除資料概觀中,我們討論了從 DataList 編輯和刪除數據的不同技術,其中有些技術涉及使用 ObjectDataSource 進行更新和刪除。 如果您採用這些技術,您可以透過 ObjectDataSource 或 Updated 事件處理程式來處理 BLL 或 Deleted DAL 的例外狀況。
步驟 1:建立可編輯的數據清單
在我們擔心處理更新工作流程期間發生的例外狀況之前,讓我們先建立可編輯的 DataList。
ErrorHandling.aspx開啟資料夾中的頁面EditDeleteDataList、將 DataList 新增至設計工具、將其ID屬性設定為 Products,然後新增名為 ProductsDataSource的新 ObjectDataSource。 將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProducts() 方法來選取記錄;將 INSERT、UPDATE 和 DELETE 索引標籤中的下拉式清單設定為 [無]。
圖 1:使用 GetProducts() 方法傳回產品資訊(按兩下以檢視完整大小的影像)
完成 ObjectDataSource 精靈之後,Visual Studio 會自動為 DataList 建立 ItemTemplate 。 將它取代為 ItemTemplate ,其中顯示每個產品名稱和價格,並包含 [編輯] 按鈕。 接下來, EditItemTemplate 使用 TextBox Web 控制項建立 ,以取得名稱和價格,以及 [更新] 和 [取消] 按鈕。 最後,將 DataList s RepeatColumns 屬性設定為 2。
這些變更之後,頁面的宣告式標記看起來應該類似下列內容。 請仔細檢查,確定 [編輯]、[取消] 和 [更新] 按鈕的屬性 CommandName 分別設定為 [編輯]、[取消] 和 [更新]。
<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的檢視狀態。
請花點時間透過瀏覽器檢視我們的進度(請參閱圖 2)。
圖 2:每個產品都包含編輯按鈕(按兩下以檢視完整大小的影像)
目前,[編輯] 按鈕只會造成回傳,它尚未讓產品可編輯。 若要啟用編輯,我們需要建立 DataList 、 EditCommandCancelCommand和 UpdateCommand 事件的事件處理程式。
EditCommand和 CancelCommand 事件只會更新 DataList 的 EditItemIndex 屬性,並將數據重新系結至 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事件處理程式更涉及一點。 它必須從ProductID集合讀取已編輯的產品,DataKeys以及 中 EditItemTemplateTextBoxes 的產品名稱和價格,然後呼叫 ProductsBLL 類別 s UpdateProduct 方法,再將 DataList 傳回其預先編輯狀態。
現在,讓我們只使用 DataListUpdateCommand事件處理程式完全相同的程式代碼。 我們將新增程序代碼,以正常處理步驟 2 中的例外狀況。
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();
}
面對可能格式不正確之單價形式的無效輸入,將引發例外狀況的非法單價值,例如 -$5.00,或遺漏產品名稱。
UpdateCommand由於事件處理程式目前未包含任何例外狀況處理程式碼,因此例外狀況會升至 ASP.NET 運行時間,而該運行時間將會顯示給終端使用者(請參閱圖 3)。
圖 3:發生未處理的例外狀況時,使用者會看到錯誤頁面
步驟 2:正常處理 UpdateCommand 事件處理程式中的例外狀況
在更新工作流程期間,事件處理程式、BLL 或 DAL 中可能會發生 UpdateCommand 例外狀況。 例如,如果使用者輸入太貴的價格, Decimal.Parse 事件處理程式中的 UpdateCommand 語句將會擲回 FormatException 例外狀況。 如果使用者省略產品名稱,或價格有負值,DAL 將會引發例外狀況。
發生例外狀況時,我們想要在頁面本身內顯示資訊訊息。 將標籤 Web 控制項新增至設定為 的頁面IDExceptionDetails。 將標籤 CssClass 的文字指派給 Warning 檔案中 Styles.css 定義的 CSS 類別,設定標籤文字以紅色、大、粗體和斜體字型顯示。
發生錯誤時,我們只想要顯示標籤一次。 也就是說,在後續回傳時,標籤的警告訊息應該會消失。 這可以藉由清除 Label 的 Text 屬性或在事件處理程式中將其屬性設定Visible為 False 來完成(如同我們在 Page_Load教學課程中處理 BLL 和 DAL 層級例外狀況一樣),或停用卷標的檢視狀態支援。 讓我們使用後者選項。
<asp:Label ID="ExceptionDetails" EnableViewState="False" CssClass="Warning"
runat="server" />
引發例外狀況時,我們會將例外狀況的詳細數據指派給 ExceptionDetails Label 控件的 Text 屬性。 由於其檢視狀態已停用,因此在後續回傳 Text 時,屬性的程式設計變更將會遺失,並還原回默認文字(空字串),從而隱藏警告訊息。
若要判斷何時引發錯誤,以便在頁面上顯示有用的訊息,我們需要將區塊新增 Try ... Catch 至 UpdateCommand 事件處理程式。 部分 Try 包含可能導致例外狀況的程序代碼,而 Catch 區塊則包含面對例外狀況執行的程序代碼。 如需念一節。
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 區塊的程序代碼就會開始執行。 擲回 DbException、 NoNullAllowedException、 ArgumentException等的例外狀況類型取決於第一次擷取錯誤的內容。 如果資料庫層級發生問題, DbException 將會擲回 。 如果針對、 、 或欄位輸入UnitPrice了非法值,UnitsInStock會擲回 ,因為我們已新增程式代碼來驗證 類別中的UnitsOnOrder這些域值(請參閱ReorderLevel教學課程)。ArgumentExceptionProductsDataTable
我們可以藉由根據攔截到的例外狀況類型來提供更實用的說明給終端使用者。 下列程式代碼在處理 ASP.NET Page 教學課程中的處理 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;
}
若要完成本教學課程,只要從DisplayExceptionDetails傳入已攔截Catch實例的 區塊呼叫 Exception 方法即可。ex
Try ... Catch已就地封鎖後,使用者會看到更豐富的錯誤訊息,如圖 4 和 5 所示。 請注意,面對例外狀況,DataList 會維持在編輯模式中。 這是因為發生例外狀況之後,控制流程會立即重新導向至 Catch 區塊,略過傳回 DataList 至其預先編輯狀態的程序代碼。
圖 4:如果使用者省略必要字段,就會顯示錯誤訊息(按兩下以檢視完整大小的影像)
圖 5:輸入負價時顯示錯誤訊息 (按兩下以檢視完整大小的影像)
總結
GridView 和 ObjectDataSource 提供後置事件處理程式,其中包含更新和刪除工作流程期間引發之任何例外狀況的相關信息,以及可設定為指出是否已處理例外狀況的屬性。 不過,當使用 DataList 並使用 BLL 時,無法使用這些功能。 相反地,我們負責實作例外狀況處理。
在本教學課程中,我們已瞭解如何將區塊新增至事件處理程式,將例外狀況處理新增至Try ... Catch可編輯的 DataList 更新工作流程UpdateCommand。 如果在更新工作流程期間引發例外狀況, Catch 區塊的程序代碼就會執行,並在標籤中 ExceptionDetails 顯示有用的資訊。
此時,DataList 不會盡一切努力防止例外狀況在一開始發生。 雖然我們知道負價會導致例外狀況,但我們尚未新增任何功能,以主動防止使用者輸入這類無效的輸入。 在下一個教學課程中,我們將瞭解如何在 中 EditItemTemplate新增驗證控件,協助減少使用者輸入無效所造成的例外狀況。
快樂的程序設計!
進一步閱讀
如需本教學課程中所討論主題的詳細資訊,請參閱下列資源:
- 例外狀況的設計方針
- 錯誤記錄模組和處理程式 (ELMAH) (用於記錄錯誤的開放原始碼連結庫)
- 適用於 .NET Framework 2.0 的企業連結庫(包括例外狀況管理應用程式區塊)
關於作者
斯科特·米切爾,七本 ASP/ASP.NET 書籍和 4GuysFromRolla.com 創始人的作者,自1998年以來一直與Microsoft Web 技術合作。 斯科特擔任獨立顧問、教練和作家。 他的最新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 可以透過 mitchell@4GuysFromRolla.com 聯絡他。
特別感謝
本教學系列已由許多熱心的評論者審閱。 本教學課程的主要檢閱者是 Ken Pespisa。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果是,請在 mitchell@4GuysFromRolla.com給我留言。