宝哥软件园

ASP.NET 2.0中的操作数据十八:处理ASP.NET BLL/DAL层中的异常

编辑:宝哥软件园 来源:互联网 时间:2021-11-01

介绍

使用分层架构在ASP.NET网络应用系统中处理数据通常遵循以下步骤:

1.确定业务逻辑层需要调用哪个方法,需要进入和退出哪些参数。这些参数可以通过硬编码设置,由程序自动设置,或者由用户输入。2.调用这个方法。3.处理结果。当调用返回数据的BLL方法时,这包括将数据绑定到数据网站服务器控件。对于修改数据的BLL方法,这包括根据返回值执行一些操作,或者正确处理第二步中引发的异常。

正如我们在上一节中看到的,ObjectDataSource控件和Data Web Server控件都为步骤1和3提供了可扩展性。例如,GridView控件在触发其RowUpdating事件之前,将其字段的值分配给ObjectDataSource的UpdateParameters集合;在ObjectDataSource完成操作后,触发RowUpdated事件。

我们已经检测到了在步骤1中触发的事件,并看到了如何使用它们来实现自定义访问参数或取消操作。在本节中,我们将关注操作完成后触发的事件。通过这些后级事件处理程序和其他程序,您可以判断操作过程中是否发生了异常,并正确处理它。最好在屏幕上显示友好的错误信息,而不是转到ASP.NET的默认错误处理页面。

为了说明这些后期事件是如何工作的,让我们创建一个页面,在可编辑的GridView中列出产品信息。更新产品时,如果抛出异常,我们的ASP.NET页面将在GridView控件的顶部显示一条短消息,指示有问题。好了,我们开始吧!

第一步:是为产品创建一个可编辑的GridView。

在本节中,我们创建了一个可编辑的GridView,它只包含两个字段,ProductName和UnitPrice。因此,与接受每个产品的字段的方法相比,有必要在ProductsBLL类的UpdateProduct方法中添加一个额外的重载,该方法只接受三个输入参数(产品名称、单价和标识)。在本节中,让我们再次练习这些技巧,并创建一个可编辑的GridView,它显示产品的名称、单位数量、单价和库存单位,但只允许名称、单价和库存单位可编辑。

为了提供这个场景,我们需要UpdateProduct方法的另一个重载,它接收四个参数:产品名称、单价、库存单位和id。向ProductsBLL类添加以下方法:

[系统。组件模型。component model . DataObjectMethodType . update,false)])公共bool UpdateProduct(字符串productName,十进制?单价,短?unitsInStock,int productID){ Northwind。产品数据表产品=适配器。getProductByProductID(ProductID);if(产品。Count==0) //未找到匹配记录,返回false返回false;北风。products row product=products[0];产品。ProductName=productNameif (unitPrice==null)产品。setunitpriceNull();其他产品。单价=单价。价值;if (unitsInStock==null)乘积。SetUnitsInStockNull();其他产品。UnitsInStock=unitsInStock。价值;//更新产品记录int rowsAffected=Adapter。更新(产品);//如果只更新了一行,则返回true,否则返回false rowsAffected==1;}完成此方法后,我们可以创建一个ASP.NET页面,允许编辑这四个产品字段。在EditInsertDelete文件夹中打开ErrorHandling.aspx页,并通过设计器向该页添加一个GridView控件。将此GridView绑定到新的ObjectDataSource控件,将Select()方法映射到ProductsBLL类的GetProducts()方法,并将Update()方法映射到刚刚创建的UpdateProduct重载。

//files.jb51.net/file_images/article/201605/2016050710105439.png

图1:重载了UpdateProduct方法,该方法接受四个输入参数。

这将创建一个包含四个参数的UpdateParameters集合的ObjectDataSource和一个包含产品每个字段的GridView。ObjectDataSource的声明将oldvaluesparametertformatstring属性标记为original_{0},这将引发异常,因为我们的BLL类没有名为original_productID的输入参数可以传入。不要忘记从声明语法中删除所有这些设置(或将它们设置为默认值:{0})。

然后,减少GridView的绑定列,使其只包括ProductName、QuantityPerUnit、UnitPrice和UnitsInStock的列。设置一些您认为必要的字段级格式(例如,更改HeaderText属性)。

在前一章中,我们已经看到了如何在只读和编辑模式下将单价绑定列格式化为货币格式。我们在这里也这样做。这需要将绑定列的DataFormatString属性设置为{0:c},将其HtmlEncode属性设置为false,并将ApplyFormatInEditMode属性设置为true,如图2所示。

//files.jb51.net/file_images/article/201605/2016050710105540.png

图2:单价绑定列被配置为显示货币金额。

要在编辑界面将单价格式化为货币,需要为GridView的RowUpdating事件创建一个事件处理程序,将货币格式的字符串转换为十进制。回顾上一节,行更新事件处理还用于检测和确保用户输入了单价值。但是,在这一部分,我们可以允许用户忽略价格列。

受保护的void GridView1 _ RowUpdating(对象发送方,GridViewUpdateEventArgs e){ if(e . new values[' unit price ']!=null)e . new values[' UnitPrice ']=十进制。解析(例如新值['单价'])。ToString(),系统。全球化.数字风格.货币);}我们的GridView包含一个QuantityPerUnit绑定列,但它仅用于显示,用户无法编辑。为此,只需将绑定列的ReadOnly属性设置为true。

//files.jb51.net/file_images/article/201605/2016050710105541.png

图3:将“数量类型”绑定列设置为只读。

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

//files.jb51.net/file_images/article/201605/2016050710105842.png

图4:删除除必要的绑定列之外的其他列,并启用编辑。

这里我们展示了产品的所有栏目,比如ProductName、QuantityPerUnit、UnitPrice和UnitsInStock;只能编辑产品名称、单价和单位库存列。

//files.jb51.net/file_images/article/201605/2016050710105843.png

图5:用户现在可以轻松编辑产品名称、价格和库存单位字段。

第二步,DAL层异常处理得当。

此时,当用户输入合法产品的名称、价格和库存单位时,我们的可编辑GridView表现非常好,当用户输入非法值时,它会导致异常。例如,如果省略ProductName值,则会引发NoNullAllowedException异常异常,因为ProdcutsRow类的ProductName属性将其AllowDBNull属性设置为false;如果数据库工作不正常,当试图连接到数据库时,会通过TableAdapter引发SqlException异常。没有任何操作,这些异常将从数据访问层出现到业务逻辑层,然后到ASP.NET页面,最后到ASP.NET运行时。

根据您的web应用程序的配置方式以及是否从localhost访问,服务器错误处理页面、详细的错误报告或用户友好的网页上会出现未处理的异常。有关ASP.NET页面如何响应未捕获异常的更多信息,请查看ASP.NET和customErrors元素中的web应用程序错误处理。图6显示了在不指定ProductName值的情况下尝试更新产品时的屏幕。这显示了通过localhost访问时的默认详细错误报告。

//files.jb51.net/file_images/article/201605/2016050710105844.png

图6:省略产品名称将显示异常详细信息。

虽然这样的异常细节在我们测试应用程序时很有用,但是当面对这样的异常呈现时,最终用户会不知所措。最终用户可能不知道NoNullAllowedException是什么,也不知道它是如何引起的。更好的方法是向用户呈现一个更友好的信息,即在尝试更新产品时出现了问题。

如果在此操作期间发生异常,对象数据源和数据网站控件的后期事件都会提供方法来发现并防止它出现在ASP.NET运行时中。在我们的示例中,让我们为GridView的RowUpdated事件创建一个事件处理程序,它确定是否触发了异常,如果是,则在Label服务器控件中显示异常的详细信息。

首先,向ASP.NET页面添加一个标签控件,将其标识属性设置为异常详细信息,并清空其文本属性。为了吸引用户意识到这个信息,将其CssClass设置为Warning,这是我们在上一章中添加到Styles.css文件中的一个CSS类。请记住,这个CSS类使标签的文本显示为红色、斜体和粗体的大字体。

//files.jb51.net/file_images/article/201605/2016050710105845.png

图7:向页面添加标签服务器控件。

因为我们希望此标签控件仅在发生异常时显示,所以在Page_Load事件处理中将它的Visible属性设置为false:

受保护的void Page_Load(对象发送方,事件参数e){ ExceptionDetails。可见=假;}使用这些代码,在第一次访问页面和随后的回发后,ExceptionDetails控件的Visible属性将被设置为false。当在GridView的RowUpdated事件处理程序中检测到DAL/BLL级别的异常时,我们将把ExceptionDetails控件的Visible属性设置为true。将显示标签,因为Web服务器控件的事件处理发生在页面生命周期中的Page_Load事件处理之后。但是,在下一次回发中,Page_Load事件处理程序会将Visible属性重置为false并再次隐藏它。

注意:我们不需要在页面加载中设置异常详细信息控件的可见属性。或者,我们可以在声明语法中将其Visible属性设置为false,并禁用视图状态(将其EnableViewState属性设置为false)。我们将在以后的章节中使用这种方法。

通过添加这个Label控件,我们的下一步是为GridView的RowUpdated事件添加一个事件处理程序。在“设计”视图中选择GridView控件,打开“属性”窗口,然后单击黄色闪电图标列出所有GridView事件。在GridView的RowUpdating事件中,我们可以看到一个条目已经存在,因为我们在本节前面为这个事件创建了一个事件处理程序。为RowUpdated事件创建事件处理程序。

//files.jb51.net/file_images/article/201605/2016050710105946.png

图8:为GridView事件创建一个事件处理程序。

注意:您也可以通过代码隐藏文件顶部的下拉列表创建这个事件处理程序。从左侧下拉列表中选择此GridView控件,并从右侧下拉列表中选择RowUpdated事件。

创建此事件处理程序会将以下代码添加到ASP.NET页面的代码隐藏类中:

受保护的void GridView 1 _ row updated(Object Sender,GridViewUpdatedEventArgs e) {}此事件处理程序的第二个输入参数是GridViewUpdatedEventArgs类型的对象,它有三个关于异常处理的属性:

异常获取更新操作期间引发的异常;如果未引发异常,此属性的值为null exception handled获取或设置一个值,该值指示更新操作期间引发的异常是否已在RowUpdated事件处理程序中处理;如果设置为false(默认值),异常将被重新引发并泄露给ASP.NET运行时保持在编辑模式如果设置为true,GridView的当前编辑行将保持编辑模式;如果设置为false(默认值),当前行将恢复为只读模式。

那么我们的代码应该检查Exception是否为空,如果不为空,就意味着执行这个操作的时候抛出了一个异常。如果是这样,我们希望:

在异常详细信息控件中显示用户友好的提示,指示异常已被处理,并保持当前行处于编辑模式。

以下代码实现了上述目的:

受保护的void GridView1_RowUpdated(对象发送方,GridViewUpdatedEventArgs e){ if(e . Exception!=null) { //显示用户友好的消息异常详细信息。可见=真;例外详细信息。文本=“更新产品时出现问题。”;if (e.Exception.InnerException!=null) {异常内部=e . Exception.InnerException如果(内部是系统。数据。公共。数据库异常)异常详细信息。文本=“我们的数据库当前遇到问题。”请稍后再试。else if (inner为NoNullAllowedException)ExceptionDetails。Text=“缺少一个或多个必填字段。”;else if(内部为ArgumentException){ string ParAmeter=((ArgumentException)内部)。ParamName例外详细信息。Text=字符串。Concat('The ',' paramName,'值非法。);} else if(内部为application exception)exception tails。Text=inner。消息;} //表示已经处理了异常e . ExceptionHandled=true//保持行处于编辑模式e . KeepInEditMode=true}}在此事件处理程序中,首先检查e.Exception是否为空。否则,将“异常详细信息”控件的“可见”属性设置为“真”,将“文本”属性设置为“更新产品时出现问题”。当前抛出的异常详细信息保存在e.Exception对象的InnerException属性中。检查这个内部异常,如果它是一个特定的类型,将一些额外的有用信息附加到ExceptionDetails标记的Text属性。最后,ExceptionHandled和KeepInEditMode属性都设置为true。

图9显示了缺少产品名称时的页面截图。图10显示了输入非法的单价值(-50)时的结果。

//files.jb51.net/file_images/article/201605/2016050710105947.png

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

//files.jb51.net/file_images/article/201605/2016050710105948.png

图10:单价值不接受负数。

通过将属性设置为,事件处理程序指示异常已被处理。因此,此异常不会传输到ASP.NET运行时。注:图9和图10显示了一种处理由不正确的用户输入引起的异常的好方法。但是,理想情况下,这些不正确的输入不应该到达业务逻辑层,因为ASP.NET页面应该确保用户的输入在调用ProductsBLL类的UpdateProduct方法之前是有效的。在下一节中,我们将了解如何向编辑和插入界面添加验证控件,以确保提交给业务逻辑层的数据遵循业务规则。验证控件不仅可以防止在用户提供有效数据之前调用UpdateProduct方法,还可以为定位数据输入问题提供更具启发性的用户体验。

在第三步骤:中,BLL层的异常被适当地处理。

当插入、更新或删除数据时,数据访问层在遇到与数据相关的错误时会抛出异常。数据库可能没有连接,必需的数据库表字段可能没有指定值,或者可能违反了表之间的约束。除了已识别的数据相关异常之外,业务逻辑层还使用异常来指示违反业务逻辑的情况。在创建业务逻辑层的部分,作为一个例子,我们添加了一个业务规则来检查原始的UpdateProduct重载。特别是,如果用户将产品标记为缺货,我们要求该产品不能是供应商提供的唯一产品。如果违反此条件,将引发ApplicationException异常。

在本节中,我们添加了一个业务规则来更新产品重载:禁止将单价字段的值设置为原始值的两倍以上。要实现这一点,请调整UpdateProduct重载,以便它可以执行此检查,并在违反规则时引发ApplicationException异常。更新方法如下:

公共bool UpdateProduct(字符串productName,十进制?单价,短?unitsInStock,int productID){ Northwind。产品数据表产品=适配器。getProductByProductID(ProductID);if(产品。Count==0) //未找到匹配记录,返回false返回false;北风。products row product=products[0];//确保价格没有超过一倍如果(单价!=null!产品。IsUnitPriceNull()) if (unitPrice产品。单价* 2)抛出新的应用异常(‘更新产品价格时,’‘新价格不能超过原价的两倍。’);产品。ProductName=productNameif (unitPrice==null)产品。setunitpriceNull();其他产品。单价=单价。价值;if (unitsInStock==null)乘积。SetUnitsInStockNull();其他产品。UnitsInStock=unitsInStock。价值;//更新产品记录int rowsAffected=Adapter。更新(产品);//如果只更新了一行,则返回true,否则返回false rowsAffected==1;}通过此修改,任何超过现有价格两倍的价格更新都将引发ApplicationException异常。就像DAL中抛出的异常一样,BLL抛出的ApplicationException可以在GridView的RowUpdated事件处理程序中检测和处理。实际上,我们现有的RowUpdated事件处理程序代码可以正确地找到这个异常,并显示ApplicationException的Message属性值。图11显示了当用户试图将产品“柴”的价格更新为50.00美元时的屏幕截图,这是原始价格19.95美元的两倍多。

//files.jb51.net/file_images/article/201605/2016050710105949.png

图11:此业务规则不接受超过产品现有价格两倍的涨价。

请注意,理想情况下,我们的业务规则不应该在UpdateProduct方法重载中,而应该在公共方法中。这是留给读者练习的。

摘要

在插入、更新或删除操作的过程中,数据Web控件和ObjectDataSource控件都包含记录当前操作的前级和后级事件。正如我们在本节和上一节中所看到的,当使用一个可编辑的GridView时,GridView的RowUpdating事件在ObjectDataSource的Updating事件之后被触发,然后update命令被发送到ObjectDataSource的隐藏对象。完成此操作后,对象数据源的更新事件将在GridView的行更新事件之后触发。

我们可以在操作之前为这些事件创建事件处理程序,目的是自定义输入参数。为操作后发生的事件创建事件处理程序,以便检测相应操作的结果。后级事件处理程序通常用于检测操作期间是否发生异常。当遇到异常时,这些后期事件处理程序可以随意处理异常。在本节中,我们已经看到了如何处理这样的异常并显示友好的错误消息。

在下一节中,我们将研究如何减少由数据格式问题引起的异常的可能性(例如,在UnitPrice中输入一个负数)。特别是,我们将研究如何将验证控件添加到编辑和插入界面。

编程快乐!

作者简介

斯科特米切尔,六本关于ASP/ASP的书的作者。NET,是4GuysFromRolla.com的创始人,自1998年以来一直使用微软的网络技术。Scott是一名独立的技术顾问、培训师和作家,最近完成了一部即将由Sams出版社出版的新作,24小时内精通ASP.NET 2.0。他的联系电子邮件是[emailprotected],也可以通过他的博客http://ScottOnWriting.NET联系到他。

更多资讯
游戏推荐
更多+