音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

在ASP.NET 2.0中操作数据之十八:在ASP.NET页面中处理BLL/DAL层的异常
日期:2021-09-07 22:13:07   来源:脚本之家

导言

在一个使用了分层体系架构的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重载。

//img.jbzj.com/file_images/article/201605/2016050710105439.png

图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所示。

//img.jbzj.com/file_images/article/201605/2016050710105540.png

图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。

//img.jbzj.com/file_images/article/201605/2016050710105541.png

图 3: 设置QuantityPerUnit绑定列为只读

最后,从GridView的智能标记里勾选上“启用编辑”。完成了这些步骤后,ErrorHandling.aspx页面在设计视图里将如图4所示。

//img.jbzj.com/file_images/article/201605/2016050710105842.png

图 4: 删除除了必需的绑定列之外的其它列并启用编辑

在这里我们显示产品的所有列,ProductName、QuantityPerUnit、UnitPrice和UnitsInStock;不过仅仅ProductName、UnitPrice和UnitsInStock这几列可以编辑。

//img.jbzj.com/file_images/article/201605/2016050710105843.png

图 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访问时的默认详细错误报表。

//img.jbzj.com/file_images/article/201605/2016050710105844.png

图 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显示为红色、斜体、加粗的较大的字体。

//img.jbzj.com/file_images/article/201605/2016050710105845.png

图 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事件创建一个事件处理程序。

//img.jbzj.com/file_images/article/201605/2016050710105946.png

图 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)时的结果。

//img.jbzj.com/file_images/article/201605/2016050710105947.png 

图 9: ProductName绑定列必须包含一个值

//img.jbzj.com/file_images/article/201605/2016050710105948.png

图 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的两倍。

//img.jbzj.com/file_images/article/201605/2016050710105949.png

图 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与他联系。

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    ASP编程JSP编程PHP编程.NET编程python编程