导言
在一个使用了分层体系架构的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; }</div>
完成了此方法后,我们可以创建一个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); }</div>
我们的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级事件都提供了发