在ASP.NET 2.0中操作資料之十八:在ASP.NET頁面中處理BLL/DAL層的異常_自學過程

來源:互聯網
上載者:User

導言

在一個使用了分層體系架構的ASP.NET web應用系統裡處理資料,一般遵循以下幾步:

1.確定商務邏輯層需要調用哪個方法,並且需要出入哪些參數。這些參數可以通過寫入程式碼設定,程式自動設定,或者由使用者輸入。
2.調用此方法。
3.處理結果。當調用一個返回資料的BLL方法時,這包括綁定資料到Data Web伺服器控制項。而對於修改資料的BLL方法而言,這包括基於傳回值的基礎上執行某些動作,或者適當地處理在第二步中引發的異常。

  正如我們在前一節裡看到的,無論ObjectDataSource控制項還是資料Web伺服器控制項,都為第1和第3步提供了可擴充性。例如GridView控制項,觸發它的RowUpdating事件之前把它的欄位的值賦值到ObjectDataSource的UpdateParameters集合;在ObjectDataSource完成它的操作之後觸發RowUpdated事件。

  我們已經檢測到第1步中觸發的事件,並且看過了如何使用它們實現自訂出入參數或者取消操作。這一節我們將把我們的注意力轉到操作完成後所觸發的事件。通過這些post級的event handler和其它,可以判斷在操作過程中是否產生了一個異常,並且適當地處理它,在螢幕中顯示友好的錯誤資訊要優於轉到ASP.NET的預設錯誤處理頁。

  為了舉例說明這些post級事件的工作方式,讓我們建立一個頁面,它在一個可編輯的GridView中列出產品資訊。當更新一個產品時,如果引發了一個異常,我們的ASP.NET頁面會在GridView控制項的上方顯示一個簡短的資訊,說明出現了一個問題。好吧,讓我們開始!

第一步: 為產品建立一個可編輯的GridView

  這一節裡我們建立一個可編輯的GridView,它僅僅包含兩個的欄位,ProductName和UnitPrice。這需要為ProductsBLL類的UpdateProduct方法增加一個額外的重載,它僅僅接受3個輸入參數(product's name,unit price,和ID),相對於接受每一個產品的欄位的方法。在本節裡讓我們再一次練習一下這些技巧,建立一個可編輯的GridView,它顯示產品的name、quantity per unit、unit price、和units in stock,但僅僅允許name,unit price,和units in stock可編輯。

  為了提供這個情境,我們需要對UpdateProduct方法的另一個重載,它接收4個參數: product's name,unit price,units in stock和ID。在ProductsBLL類中添加下面這個方法:

[System.ComponentModel.DataObjectMethodAttribute(  System.ComponentModel.DataObjectMethodType.Update, false)]public bool UpdateProduct(string productName, decimal? unitPrice, short? unitsInStock,  int productID){  Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);  if (products.Count == 0)    // no matching record found, return false    return false;  Northwind.ProductsRow product = products[0];  product.ProductName = productName;  if (unitPrice == null) product.SetUnitPriceNull();   else product.UnitPrice = unitPrice.Value;  if (unitsInStock == null) product.SetUnitsInStockNull();   else product.UnitsInStock = unitsInStock.Value;  // Update the product record  int rowsAffected = Adapter.Update(product);  // Return true if precisely one row was updated, otherwise false  return rowsAffected == 1;}

  完成了此方法後,我們可以建立一個ASP.NET頁面,它允許編輯這四個產品欄位。開啟EditInsertDelete檔案夾裡的ErrorHandling.aspx頁面,並通過設計器添加一個GridView控制項到頁面中。綁定這個GridView到一個新的ObjectDataSource控制項,映射Select()方法到ProductsBLL類的GetProducts()方法,方法Update()映射到剛剛建立的UpdateProduct重載。

圖1: 使用UpdateProduct方法重載,它接受四個輸入參數

  這將建立一個ObjectDataSource,它包含四個參數的UpdateParameters集合,還有一個一個GridView,它包含產品的每一個欄位。ObjectDataSource的聲明標記給OldValuesParameterFormatString屬性賦值為original_{0},它將引發一個異常,因為我們的BLL類沒有一個名為original_productID的輸入參數需要傳入。別忘了從聲明文法裡把這些設定通通刪除(或者把它們設定為預設值:{0})。

  然後,減少GridView的繫結資料行,僅包含ProductName,QuantityPerUnit,UnitPrice和UnitsInStock這幾列。隨意設定一些你認為必要的欄位級的格式(例如更改HeaderText屬性)。

  在之前的章節裡我們已經看過了如何在唯讀和編輯兩種模式下格式化UnitPrice繫結資料行為貨幣格式。在這裡我們同樣這樣做。這需要設定繫結資料行的DataFormatString屬性為{0:c},它的HtmlEncode屬性為false,還有它的ApplyFormatInEditMode屬性為true,如圖2所示。

圖2: UnitPrice繫結資料行配置為顯示一個貨幣金額

  要在編輯介面將UnitPrice格式化為貨幣,這需要為GridView的RowUpdating事件建立一個事件處理,它將一個貨幣格式的字串轉換成decimal。回想上一節,RowUpdating事件處理也用來檢測並確保使用者輸入的是一個UnitPrice的值。不過,本節我們可以允許使用者忽略price列。

protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e){  if (e.NewValues["UnitPrice"] != null)    e.NewValues["UnitPrice"] =decimal.Parse(e.NewValues["UnitPrice"].ToString(),      System.Globalization.NumberStyles.Currency);}

  我們的GridView包含一個QuantityPerUnit繫結資料行,但它僅僅用作顯示,不能被使用者編輯。為了實現這一點,只需要簡單地將該繫結資料行的ReadOnly屬性設定為true。

圖 3: 設定QuantityPerUnit繫結資料行為唯讀

最後,從GridView的智能標記裡勾選上“啟用編輯”。完成了這些步驟後,ErrorHandling.aspx頁面在設計檢視裡將如圖4所示。

圖 4: 刪除除了必需的繫結資料行之外的其它列並啟用編輯

在這裡我們顯示產品的所有列,ProductName、QuantityPerUnit、UnitPrice和UnitsInStock;不過僅僅ProductName、UnitPrice和UnitsInStock這幾列可以編輯。

圖 5: 使用者現在可以很方便地編輯Products' Names、Prices和Units In Stock欄位

第二步:適當地處理DAL層異常

  這時我們的可編輯的GridView在使用者輸入合法的product's name、price和units in stock時表現極佳,輸入不合法的值時則導致一個異常。例如,遺漏了ProductName值則引發拋出一個NoNullAllowedException異常,因為ProdcutsRow類的ProductName屬性設定了它的AllowDBNull屬性為false;如果資料庫不正常運作,則在試圖串連資料庫時通過TableAdapter拋出一個SqlException異常。沒有任何的動作,這些異常都會從資料訪問層冒出到商務邏輯層,然後到ASP.NET頁面,最後到ASP.NET運行時。

  取決於你的web應用程式如何配置以及是否從localhost訪問該應用,一個未處理的例外狀況會出現在一類伺服器錯誤處理頁,一個詳細的錯誤判表,或者一個對方便使用的web頁面。查看Web Application Error Handling in ASP.NET 和 customErrors Element 獲得更多的關於ASP.NET頁面如何響應一個未捕獲的異常的相關資訊。
圖6展示的是試圖不指定ProductName的值更新一個產品時螢幕的狀況。這顯示的是通過localhost訪問時的預設詳細錯誤判表。

圖 6: 省略Product's Name將顯示異常明細

  雖然這樣的異常明細在我們測試應用程式的時候是很有用的,然而當一個終端使用者面對這樣的異常呈現時卻是無所適從的。一個終端使用者很可能並不知道NoNullAllowedException是什麼,或者它是如何引起的。更好的方法是呈現給使用者一個更友好的資訊說明試圖更新產品時出現了問題。

  如果在執行這項操作時出現了一個異常,ObjectDataSource 和資料Web控制項的post級事件都提供了發現並不讓它出現在ASP.NET運行時的方法。在我們的例子裡,讓我們為GridView的RowUpdated事件建立一個事件處理常式,它判斷是否激發了一個異常,如果是,則在一個Label伺服器控制項中顯示異常詳細資料。

  首先,添加一個Label控制項到ASP.NET頁面,設定它的ID屬性為ExceptionDetails並清空它的Text屬性。為了吸引使用者的實現到此資訊,設定其CssClass為Warning,這是我們在之前的章節裡添加到Styles.css檔案的一個CSS類別。記得這個CSS類別讓Label的text顯示為紅色、斜體、加粗的較大的字型。

圖 7: 添加一個Label伺服器控制項到頁面

因為我們希望這個Label控制項僅在異常出現時顯示,在Page_Load事件處理中設定它的Visible屬性為false:

protected void Page_Load(object sender, EventArgs e){  ExceptionDetails.Visible = false;}

  通過這些代碼,當第一次訪問頁面和隨後的回傳後,ExceptionDetails控制項的Visible屬性都將被設定為false。當在GridView的RowUpdated事件處理常式中檢測到一個DAL/BLL層的異常時,我們將設定ExceptionDetails控制項的Visible屬性為true。因為頁面生命週期裡Web伺服器控制項的事件處理出現在Page_Load事件處理之後,該Label將會顯示。不過,下一次回傳,Page_Load事件處理將重新將Visible屬性設定回false,再次隱藏它。

  注意: 我們也可以不必在Page_Load裡設定ExceptionDetails控制項的Visible屬性,作為另一種選擇,可以在聲明文法裡設定其Visible屬性為false並禁用檢視狀態(設定它的EnableViewState屬性為false)。我們將在以後的章節裡使用這種方法。

  通過添加這個Label控制項,我們下一步是為GridView的RowUpdated事件添加一個事件處理常式。在設計檢視中選中GridView控制項,開啟屬性視窗,點擊黃色閃電狀表徵圖,列出GridView的所有事件。在GridView的RowUpdating事件裡我們可以看到已經存在一個入口,因為我們在本節較早的時候已經為此事件建立了一個事件處理常式。為RowUpdated事件建立一個事件處理常式。

圖 8: 為GridView的事件建立一個事件處理

  注意: 你也可以通過程式碼後置檔案頂處的下拉式清單建立這個事件處理。從左邊的下拉式清單中選擇這個GridView控制項,並從右邊的下拉式清單中選擇RowUpdated事件。

建立這個事件處理將添加下面這些代碼到ASP.NET頁面的程式碼後置類別中:

 

protected void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e){}

這個事件處理常式的第二個輸入參數是一個GridViewUpdatedEventArgs類型的對象,它有三個關於處理異常的屬性:

·Exception –擷取更新操作過程中引發的異常;如果沒有拋出異常,該屬性的值為null
·ExceptionHandled –擷取或設定一個值,它指示在更新操作過程中所引發的異常是否已在RowUpdated事件處理常式中得到處理;如果設為false(預設值),該異常將被重新引發,漏出到ASP.NET運行時
·KeepInEditMode – 如果設定為true,GridView當前編輯行將維持在編輯模式;如果設定為false(預設值),當前行將恢複到唯讀模式

那麼我們的代碼應該檢測Exception是否為null,不是null則意味著執行此操作時引發了一個異常。如果是這樣,我們則希望:

·在ExceptionDetails控制項中顯示一個對方便使用的提示資訊
·指示異常已經被處理
·讓當前行保持編輯模式

下面的代碼實現了上述的目的:

protected void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e){  if (e.Exception != null)  {    // Display a user-friendly message    ExceptionDetails.Visible = true;    ExceptionDetails.Text = "There was a problem updating the product. ";    if (e.Exception.InnerException != null)    {      Exception inner = e.Exception.InnerException;      if (inner is System.Data.Common.DbException)        ExceptionDetails.Text +=          "Our database is currently experiencing problems." +          "Please try again later.";      else if (inner is NoNullAllowedException)        ExceptionDetails.Text +=          "There are one or more required fields that are missing.";      else if (inner is ArgumentException)      {        string paramName = ((ArgumentException)inner).ParamName;        ExceptionDetails.Text +=          string.Concat("The ", paramName, " value is illegal.");      }      else if (inner is ApplicationException)        ExceptionDetails.Text += inner.Message;    }    // Indicate that the exception has been handled    e.ExceptionHandled = true;    // Keep the row in edit mode    e.KeepInEditMode = true;  }}

  在這個事件處理常式中,首先檢測e.Exception是否為null。如果不是,設定ExceptionDetails控制項的Visible屬性為true、設定它的Text屬性為“There was a problem updating the product.”。當前拋出的異常詳細資料則儲存在e.Exception對象的InnerException屬性裡。檢查這個內部異常,如果它是特定的類型,則把一些額外的有用的資訊附加到ExceptionDetails標籤的Text屬性。最後,ExceptionHandled和KeepInEditMode屬性都設定為true。

圖9展示的是遺漏了產品名稱時的頁面的截屏;圖10則顯示輸入一個不合法的UnitPrice值(-50)時的結果。

 

圖 9: ProductName繫結資料行必須包含一個值

圖 10: UnitPrice值不接受負數

  通過設定屬性為,事件處理常式指示該異常已經被處理。因此,這個異常不會傳送到ASP.NET運行時。
  注意: 圖9和圖10顯示了一種得體的方式處理不正確的使用者輸入所引發的異常。可是,更理想地,這些不正確的輸入不應該到達商務邏輯層,因為ASP.NET頁面應該在調用ProductsBLL類的UpdateProduct方法之前就確保使用者的輸入是有效。我們在下一節裡將會看看如何添加validation控制項到編輯和插入介面從而保證提交到商務邏輯層的資料遵循商務規則。validation控制項不但可以阻止調用UpdateProduct方法直到使用者提供有效資料,還可以為定位元據輸入問題提供一個更充滿提示性的使用者體驗。

第三步: 適當地處理BLL層異常

  當插入、更新或刪除資料時,面對一個資料相關的錯誤時資料訪問層會拋出一個異常。資料庫可能未連線,一個必需的資料庫表欄位可能未指定值,或者違反了某個表間約束。除了確定的資料相關的異常外,商務邏輯層也使用異常指示違反了商務邏輯。在建立一個商務邏輯層 這一節裡,作為例子,我們添加了一個商務規則檢查最初的UpdateProduct重載。特別地,如果使用者標記一個產品為停止供應,我們要求這個產品不能是該供應商唯一供應的產品。如果違反了這個條件,拋出一個ApplicationException異常。

  在這一節裡,我們給UpdateProduct重載增加一個商務規則:禁止把UnitPrice欄位的值設定為超過原來的兩倍。為了實現這一點,調整UpdateProduct重載以使它可以執行這個檢查並且在違反該規則時拋出一個ApplicationException異常。此更新方法如下:

public bool UpdateProduct(string productName, decimal? unitPrice, short? unitsInStock,  int productID){  Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);  if (products.Count == 0)    // no matching record found, return false    return false;  Northwind.ProductsRow product = products[0];  // Make sure the price has not more than doubled  if (unitPrice != null && !product.IsUnitPriceNull())    if (unitPrice > product.UnitPrice * 2)     throw new ApplicationException(      "When updating a product price," +      " the new price cannot exceed twice the original price.");  product.ProductName = productName;  if (unitPrice == null) product.SetUnitPriceNull();   else product.UnitPrice = unitPrice.Value;  if (unitsInStock == null) product.SetUnitsInStockNull();   else product.UnitsInStock = unitsInStock.Value;  // Update the product record  int rowsAffected = Adapter.Update(product);  // Return true if precisely one row was updated, otherwise false  return rowsAffected == 1;}

  通過這個修改,任何超過現有價格兩倍的價格更新都回引發一個ApplicationException異常被拋出。就像DAL中引發的異常一樣,這個BLL引發的ApplicationException異常可以在GridView的RowUpdated事件處理常式中被偵測並處理。實際上,我們已有的RowUpdated事件處理常式的代碼可以正確地發現到這個異常並顯示ApplicationException的Message屬性的值。圖11顯示的是當一個使用者試圖將產品“Chai”的價格更新為$50.00時的截屏,這超過了它原有價格$19.95的兩倍。

圖 11: 這個商務規則不接受價格增長超出產品現有價格的兩倍

注意: 理想化地我們的商務規則不應該在UpdateProduct方法重載裡而應該在一個公用的方法中。這留作讀者練習。

總結

  在插入、更新或刪除操作的過程中,資料Web控制項和ObjectDataSource控制項都包含了pre- 和post-級的事件,它們記錄著當前的操作。正如我們在本節和前面的一節裡所看到的,當使用一個可編輯的GridView時,GridView的RowUpdating事件在ObjectDataSource的Updating事件之後觸發,此時update命令發送到ObjectDataSource的隱含對象。完成了此操作,在GridView的RowUpdated事件之後,觸發ObjectDataSource的Updated事件。

  我們可以為這些發生在操作之前的事件建立事件處理常式,目的是自訂輸入參數;為發生在
操作之後的事件建立事件處理,目的是檢測和相應操作的結果。Post-level的事件處理常式通常用作偵測在操作過程中是否出現了一個異常。當面對一個異常時,這些post-level的事件處理常式可以隨意地處理該異常。在本節裡我們看過了如何處理這樣的一個異常,顯示一個友好的錯誤提示資訊。

  在下一節裡我們將看看如何降低因資料格式的問題引起異常的可能性(例如在UnitPrice輸入一個負數)。特別地,我們將看看如何添加validation控制項到編輯和插入介面。

祝編程快樂!

作者簡介

Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用微軟Web技術。Scott是個獨立的技 術諮詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯絡電郵為mitchell@4guysfromrolla.com,也可以通過他的部落格http://ScottOnWriting.NET與他聯絡。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.