主頁 > 知識庫 > 在ASP.NET 2.0中操作數(shù)據(jù)之十八:在ASP.NET頁面中處理BLL/DAL層的異常

在ASP.NET 2.0中操作數(shù)據(jù)之十八:在ASP.NET頁面中處理BLL/DAL層的異常

熱門標(biāo)簽:百應(yīng)電話機(jī)器人外呼系統(tǒng) 外呼電話機(jī)器人成本 網(wǎng)絡(luò)電話外呼系統(tǒng)上海 臨沂智能電話機(jī)器人加盟 地圖標(biāo)注軟件免費(fèi)下載 蘇州如何辦理400電話 聯(lián)通官網(wǎng)400電話辦理 400電話辦理怎么樣 西寧呼叫中心外呼系統(tǒng)線路商

導(dǎo)言

在一個使用了分層體系架構(gòu)的ASP.NET web應(yīng)用系統(tǒng)里處理數(shù)據(jù),一般遵循以下幾步:

1.確定業(yè)務(wù)邏輯層需要調(diào)用哪個方法,并且需要出入哪些參數(shù)。這些參數(shù)可以通過硬編碼設(shè)置,程序自動設(shè)定,或者由用戶輸入。
2.調(diào)用此方法。
3.處理結(jié)果。當(dāng)調(diào)用一個返回數(shù)據(jù)的BLL方法時,這包括綁定數(shù)據(jù)到Data Web服務(wù)器控件。而對于修改數(shù)據(jù)的BLL方法而言,這包括基于返回值的基礎(chǔ)上執(zhí)行某些動作,或者適當(dāng)?shù)靥幚碓诘诙街幸l(fā)的異常。

  正如我們在前一節(jié)里看到的,無論ObjectDataSource控件還是數(shù)據(jù)Web服務(wù)器控件,都為第1和第3步提供了可擴(kuò)展性。例如GridView控件,觸發(fā)它的RowUpdating事件之前把它的字段的值賦值到ObjectDataSource的UpdateParameters集合;在ObjectDataSource完成它的操作之后觸發(fā)RowUpdated事件。

  我們已經(jīng)檢測到第1步中觸發(fā)的事件,并且看過了如何使用它們實現(xiàn)自定義出入?yún)?shù)或者取消操作。這一節(jié)我們將把我們的注意力轉(zhuǎn)到操作完成后所觸發(fā)的事件。通過這些post級的event handler和其它,可以判斷在操作過程中是否產(chǎn)生了一個異常,并且適當(dāng)?shù)靥幚硭?,在屏幕中顯示友好的錯誤信息要優(yōu)于轉(zhuǎn)到ASP.NET的默認(rèn)錯誤處理頁。

  為了舉例說明這些post級事件的工作方式,讓我們創(chuàng)建一個頁面,它在一個可編輯的GridView中列出產(chǎn)品信息。當(dāng)更新一個產(chǎn)品時,如果引發(fā)了一個異常,我們的ASP.NET頁面會在GridView控件的上方顯示一個簡短的信息,說明出現(xiàn)了一個問題。好吧,讓我們開始!

第一步: 為產(chǎn)品創(chuàng)建一個可編輯的GridView

  這一節(jié)里我們創(chuàng)建一個可編輯的GridView,它僅僅包含兩個的字段,ProductName和UnitPrice。這需要為ProductsBLL類的UpdateProduct方法增加一個額外的重載,它僅僅接受3個輸入?yún)?shù)(product's name,unit price,和ID),相對于接受每一個產(chǎn)品的字段的方法。在本節(jié)里讓我們再一次練習(xí)一下這些技巧,創(chuàng)建一個可編輯的GridView,它顯示產(chǎn)品的name、quantity per unit、unit price、和units in stock,但僅僅允許name,unit price,和units in stock可編輯。

  為了提供這個場景,我們需要對UpdateProduct方法的另一個重載,它接收4個參數(shù): 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;
}

  完成了此方法后,我們可以創(chuàng)建一個ASP.NET頁面,它允許編輯這四個產(chǎn)品字段。打開EditInsertDelete文件夾里的ErrorHandling.aspx頁面,并通過設(shè)計器添加一個GridView控件到頁面中。綁定這個GridView到一個新的ObjectDataSource控件,映射Select()方法到ProductsBLL類的GetProducts()方法,方法Update()映射到剛剛創(chuàng)建的UpdateProduct重載。

圖1: 使用UpdateProduct方法重載,它接受四個輸入?yún)?shù)

  這將創(chuàng)建一個ObjectDataSource,它包含四個參數(shù)的UpdateParameters集合,還有一個一個GridView,它包含產(chǎn)品的每一個字段。ObjectDataSource的聲明標(biāo)記給OldValuesParameterFormatString屬性賦值為original_{0},它將引發(fā)一個異常,因為我們的BLL類沒有一個名為original_productID的輸入?yún)?shù)需要傳入。別忘了從聲明語法里把這些設(shè)置通通刪除(或者把它們設(shè)置為默認(rèn)值:{0})。

  然后,減少GridView的綁定列,僅包含ProductName,QuantityPerUnit,UnitPrice和UnitsInStock這幾列。隨意設(shè)置一些你認(rèn)為必要的字段級的格式(例如更改HeaderText屬性)。

  在之前的章節(jié)里我們已經(jīng)看過了如何在只讀和編輯兩種模式下格式化UnitPrice綁定列為貨幣格式。在這里我們同樣這樣做。這需要設(shè)置綁定列的DataFormatString屬性為{0:c},它的HtmlEncode屬性為false,還有它的ApplyFormatInEditMode屬性為true,如圖2所示。

圖2: UnitPrice綁定列配置為顯示一個貨幣金額

  要在編輯界面將UnitPrice格式化為貨幣,這需要為GridView的RowUpdating事件創(chuàng)建一個事件處理,它將一個貨幣格式的字符串轉(zhuǎn)換成decimal?;叵肷弦还?jié),RowUpdating事件處理也用來檢測并確保用戶輸入的是一個UnitPrice的值。不過,本節(jié)我們可以允許用戶忽略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綁定列,但它僅僅用作顯示,不能被用戶編輯。為了實現(xiàn)這一點(diǎn),只需要簡單地將該綁定列的ReadOnly屬性設(shè)置為true。

圖 3: 設(shè)置QuantityPerUnit綁定列為只讀

最后,從GridView的智能標(biāo)記里勾選上“啟用編輯”。完成了這些步驟后,ErrorHandling.aspx頁面在設(shè)計視圖里將如圖4所示。

圖 4: 刪除除了必需的綁定列之外的其它列并啟用編輯

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

圖 5: 用戶現(xiàn)在可以很方便地編輯Products' Names、Prices和Units In Stock字段

第二步:適當(dāng)?shù)靥幚鞤AL層異常

  這時我們的可編輯的GridView在用戶輸入合法的product's name、price和units in stock時表現(xiàn)極佳,輸入不合法的值時則導(dǎo)致一個異常。例如,遺漏了ProductName值則引發(fā)拋出一個NoNullAllowedException異常,因為ProdcutsRow類的ProductName屬性設(shè)置了它的AllowDBNull屬性為false;如果數(shù)據(jù)庫不正常運(yùn)作,則在試圖連接數(shù)據(jù)庫時通過TableAdapter拋出一個SqlException異常。沒有任何的動作,這些異常都會從數(shù)據(jù)訪問層冒出到業(yè)務(wù)邏輯層,然后到ASP.NET頁面,最后到ASP.NET運(yùn)行時。

  取決于你的web應(yīng)用程序如何配置以及是否從localhost訪問該應(yīng)用,一個未經(jīng)處理的異常會出現(xiàn)在一類服務(wù)器錯誤處理頁,一個詳細(xì)的錯誤報表,或者一個對用戶友好的web頁面。查看Web Application Error Handling in ASP.NET 和 customErrors Element 獲得更多的關(guān)于ASP.NET頁面如何響應(yīng)一個未捕獲的異常的相關(guān)信息。
圖6展示的是試圖不指定ProductName的值更新一個產(chǎn)品時屏幕的狀況。這顯示的是通過localhost訪問時的默認(rèn)詳細(xì)錯誤報表。

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

  雖然這樣的異常明細(xì)在我們測試應(yīng)用程序的時候是很有用的,然而當(dāng)一個最終用戶面對這樣的異常呈現(xiàn)時卻是無所適從的。一個最終用戶很可能并不知道NoNullAllowedException是什么,或者它是如何引起的。更好的方法是呈現(xiàn)給用戶一個更友好的信息說明試圖更新產(chǎn)品時出現(xiàn)了問題。

  如果在執(zhí)行這項操作時出現(xiàn)了一個異常,ObjectDataSource 和數(shù)據(jù)Web控件的post級事件都提供了發(fā)現(xiàn)并不讓它出現(xiàn)在ASP.NET運(yùn)行時的方法。在我們的例子里,讓我們?yōu)镚ridView的RowUpdated事件創(chuàng)建一個事件處理程序,它判斷是否激發(fā)了一個異常,如果是,則在一個Label服務(wù)器控件中顯示異常詳細(xì)信息。

  首先,添加一個Label控件到ASP.NET頁面,設(shè)置它的ID屬性為ExceptionDetails并清空它的Text屬性。為了吸引用戶的實現(xiàn)到此信息,設(shè)置其CssClass為Warning,這是我們在之前的章節(jié)里添加到Styles.css文件的一個CSS類別。記得這個CSS類別讓Label的text顯示為紅色、斜體、加粗的較大的字體。

圖 7: 添加一個Label服務(wù)器控件到頁面

因為我們希望這個Label控件僅在異常出現(xiàn)時顯示,在Page_Load事件處理中設(shè)置它的Visible屬性為false:

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

  通過這些代碼,當(dāng)?shù)谝淮卧L問頁面和隨后的回傳后,ExceptionDetails控件的Visible屬性都將被設(shè)置為false。當(dāng)在GridView的RowUpdated事件處理程序中檢測到一個DAL/BLL層的異常時,我們將設(shè)置ExceptionDetails控件的Visible屬性為true。因為頁面生命周期里Web服務(wù)器控件的事件處理出現(xiàn)在Page_Load事件處理之后,該Label將會顯示。不過,下一次回傳,Page_Load事件處理將重新將Visible屬性設(shè)置回false,再次隱藏它。

  注意: 我們也可以不必在Page_Load里設(shè)置ExceptionDetails控件的Visible屬性,作為另一種選擇,可以在聲明語法里設(shè)置其Visible屬性為false并禁用視圖狀態(tài)(設(shè)置它的EnableViewState屬性為false)。我們將在以后的章節(jié)里使用這種方法。

  通過添加這個Label控件,我們下一步是為GridView的RowUpdated事件添加一個事件處理程序。在設(shè)計視圖中選中GridView控件,打開屬性窗口,點(diǎn)擊黃色閃電狀圖標(biāo),列出GridView的所有事件。在GridView的RowUpdating事件里我們可以看到已經(jīng)存在一個入口,因為我們在本節(jié)較早的時候已經(jīng)為此事件創(chuàng)建了一個事件處理程序。為RowUpdated事件創(chuàng)建一個事件處理程序。

圖 8: 為GridView的事件創(chuàng)建一個事件處理

  注意: 你也可以通過代碼隱藏文件頂處的下拉列表創(chuàng)建這個事件處理。從左邊的下拉列表中選擇這個GridView控件,并從右邊的下拉列表中選擇RowUpdated事件。

創(chuàng)建這個事件處理將添加下面這些代碼到ASP.NET頁面的代碼隱藏類中:

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

這個事件處理程序的第二個輸入?yún)?shù)是一個GridViewUpdatedEventArgs類型的對象,它有三個關(guān)于處理異常的屬性:

·Exception –獲取更新操作過程中引發(fā)的異常;如果沒有拋出異常,該屬性的值為null
·ExceptionHandled –獲取或設(shè)置一個值,它指示在更新操作過程中所引發(fā)的異常是否已在RowUpdated事件處理程序中得到處理;如果設(shè)為false(默認(rèn)值),該異常將被重新引發(fā),漏出到ASP.NET運(yùn)行時
·KeepInEditMode – 如果設(shè)置為true,GridView當(dāng)前編輯行將維持在編輯模式;如果設(shè)置為false(默認(rèn)值),當(dāng)前行將恢復(fù)到只讀模式

那么我們的代碼應(yīng)該檢測Exception是否為null,不是null則意味著執(zhí)行此操作時引發(fā)了一個異常。如果是這樣,我們則希望:

·在ExceptionDetails控件中顯示一個對用戶友好的提示信息
·指示異常已經(jīng)被處理
·讓當(dāng)前行保持編輯模式

下面的代碼實現(xiàn)了上述的目的:

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。如果不是,設(shè)置ExceptionDetails控件的Visible屬性為true、設(shè)置它的Text屬性為“There was a problem updating the product.”。當(dāng)前拋出的異常詳細(xì)信息則保存在e.Exception對象的InnerException屬性里。檢查這個內(nèi)部異常,如果它是特定的類型,則把一些額外的有用的信息附加到ExceptionDetails標(biāo)簽的Text屬性。最后,ExceptionHandled和KeepInEditMode屬性都設(shè)置為true。

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

 

圖 9: ProductName綁定列必須包含一個值

圖 10: UnitPrice值不接受負(fù)數(shù)

  通過設(shè)置屬性為,事件處理程序指示該異常已經(jīng)被處理。因此,這個異常不會傳送到ASP.NET運(yùn)行時。
  注意: 圖9和圖10顯示了一種得體的方式處理不正確的用戶輸入所引發(fā)的異常??墒牵硐氲?,這些不正確的輸入不應(yīng)該到達(dá)業(yè)務(wù)邏輯層,因為ASP.NET頁面應(yīng)該在調(diào)用ProductsBLL類的UpdateProduct方法之前就確保用戶的輸入是有效的。我們在下一節(jié)里將會看看如何添加validation控件到編輯和插入界面從而保證提交到業(yè)務(wù)邏輯層的數(shù)據(jù)遵循業(yè)務(wù)規(guī)則。validation控件不但可以阻止調(diào)用UpdateProduct方法直到用戶提供有效的數(shù)據(jù),還可以為定位數(shù)據(jù)輸入問題提供一個更充滿提示性的用戶體驗。

第三步: 適當(dāng)?shù)靥幚鞡LL層異常

  當(dāng)插入、更新或刪除數(shù)據(jù)時,面對一個數(shù)據(jù)相關(guān)的錯誤時數(shù)據(jù)訪問層會拋出一個異常。數(shù)據(jù)庫可能未連線,一個必需的數(shù)據(jù)庫表字段可能未指定值,或者違反了某個表間約束。除了確定的數(shù)據(jù)相關(guān)的異常外,業(yè)務(wù)邏輯層也使用異常指示違反了業(yè)務(wù)邏輯。在創(chuàng)建一個業(yè)務(wù)邏輯層 這一節(jié)里,作為例子,我們添加了一個業(yè)務(wù)規(guī)則檢查最初的UpdateProduct重載。特別地,如果用戶標(biāo)記一個產(chǎn)品為停止供應(yīng),我們要求這個產(chǎn)品不能是該供應(yīng)商唯一供應(yīng)的產(chǎn)品。如果違反了這個條件,拋出一個ApplicationException異常。

  在這一節(jié)里,我們給UpdateProduct重載增加一個業(yè)務(wù)規(guī)則:禁止把UnitPrice字段的值設(shè)置為超過原來的兩倍。為了實現(xiàn)這一點(diǎn),調(diào)整UpdateProduct重載以使它可以執(zhí)行這個檢查并且在違反該規(guī)則時拋出一個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;
}

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

圖 11: 這個業(yè)務(wù)規(guī)則不接受價格增長超出產(chǎn)品現(xiàn)有價格的兩倍

注意: 理想化地我們的業(yè)務(wù)規(guī)則不應(yīng)該在UpdateProduct方法重載里而應(yīng)該在一個公共的方法中。這留作讀者練習(xí)。

總結(jié)

  在插入、更新或刪除操作的過程中,數(shù)據(jù)Web控件和ObjectDataSource控件都包含了pre- 和post-級的事件,它們記錄著當(dāng)前的操作。正如我們在本節(jié)和前面的一節(jié)里所看到的,當(dāng)使用一個可編輯的GridView時,GridView的RowUpdating事件在ObjectDataSource的Updating事件之后觸發(fā),此時update命令發(fā)送到ObjectDataSource的隱含對象。完成了此操作,在GridView的RowUpdated事件之后,觸發(fā)ObjectDataSource的Updated事件。

  我們可以為這些發(fā)生在操作之前的事件創(chuàng)建事件處理程序,目的是自定義輸入?yún)?shù);為發(fā)生在
操作之后的事件創(chuàng)建事件處理,目的是檢測和相應(yīng)操作的結(jié)果。Post-level的事件處理程序通常用作偵測在操作過程中是否出現(xiàn)了一個異常。當(dāng)面對一個異常時,這些post-level的事件處理程序可以隨意地處理該異常。在本節(jié)里我們看過了如何處理這樣的一個異常,顯示一個友好的錯誤提示信息。

  在下一節(jié)里我們將看看如何降低因數(shù)據(jù)格式的問題引起異常的可能性(例如在UnitPrice輸入一個負(fù)數(shù))。特別地,我們將看看如何添加validation控件到編輯和插入界面。

祝編程快樂!

作者簡介

Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來一直應(yīng)用微軟Web技術(shù)。Scott是個獨(dú)立的技 術(shù)咨詢顧問,培訓(xùn)師,作家,最近完成了將由Sams出版社出版的新作,24小時內(nèi)精通ASP.NET 2.0。他的聯(lián)系電郵為mitchell@4guysfromrolla.com,也可以通過他的博客http://ScottOnWriting.NET與他聯(lián)系。

您可能感興趣的文章:
  • asp.net服務(wù)器上幾種常見異常的解決方案.
  • 在 .NET Framework 2.0 中未處理的異常導(dǎo)致基于 ASP.NET 的應(yīng)用程序意外退出
  • ASP.NET生成eurl.axd Http異常錯誤的處理方法
  • asp.net Http異常eurl.axd出錯信息解決方法
  • Asp.net Mvc 身份驗證、異常處理、權(quán)限驗證(攔截器)實現(xiàn)代碼
  • ASP.NET mvc異常處理的方法示例介紹
  • asp.net 錯誤:0x8007000B 異常的解決方法
  • asp.net開發(fā)中常見公共捕獲異常方式總結(jié)(附源碼)
  • ASP.NET MVC異常處理模塊詳解
  • 在ASP.NET 2.0中操作數(shù)據(jù)之三十八:處理BLL和DAL的異常

標(biāo)簽:清遠(yuǎn) 慶陽 甘肅 海西 聊城 臨夏 中衛(wèi)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《在ASP.NET 2.0中操作數(shù)據(jù)之十八:在ASP.NET頁面中處理BLL/DAL層的異?!?,本文關(guān)鍵詞  在,ASP.NET,2.0,中,操作,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《在ASP.NET 2.0中操作數(shù)據(jù)之十八:在ASP.NET頁面中處理BLL/DAL層的異?!废嚓P(guān)的同類信息!
  • 本頁收集關(guān)于在ASP.NET 2.0中操作數(shù)據(jù)之十八:在ASP.NET頁面中處理BLL/DAL層的異常的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章