Introduction
Working with data in a asp.net Web application that uses a layered architecture, typically follows these steps:
1. Determine which method the business logic layer needs to invoke and what parameters to access. These parameters can be set by hard coding, the program is set automatically, or the user enters it.
2. Call this method.
3. Processing results. When you call a BLL method that returns data, this includes binding data to the Web server control. For BLL methods that modify data, this includes performing certain actions based on the return value, or appropriately handling the exception that is thrown in the second step.
As we've seen in the previous section, both the ObjectDataSource control and the data Web server control provide extensibility for steps 1th and 3rd. For example, the GridView control, which triggers its RowUpdating event to assign the value of its field to the ObjectDataSource UpdateParameters collection , triggering the RowUpdated event after ObjectDataSource completes its operation.
We have detected the events triggered in step 1th and have seen how to use them to implement custom entry or exit parameters or cancel the operation. In this section we will turn our attention to the events triggered by the completion of the operation. These post-level event handler and others allow you to determine whether an exception was generated during the operation, and handle it appropriately, displaying friendly error messages on the screen better than the default error-handling page to go to asp.net.
To illustrate how these post-level events work, let's create a page that lists product information in an editable gridview. When a product is updated, if an exception is thrown, our asp.net page displays a short message above the GridView control, stating that a problem has occurred. All right, let's get started!
First step: Create an editable gridview for the product
In this section we create an editable GridView, which contains only two fields, ProductName and UnitPrice. This requires an additional overload for the UpdateProduct method of the Productsbll class, which accepts only 3 input parameters (product ' Name,unit price, and ID), relative to the method that accepts the fields for each product. In this section, let's practice these techniques again, creating an editable gridview that displays the name of the product, quantity per unit, unit price, and units in the stock, but only allows name,unit price, and units in the stock editable.
To provide this scenario, we need another overload of the UpdateProduct method, which receives 4 parameters: Product ' Name,unit price,units in-Stock and ID. Add the following method to the PRODUCTSBLL class:
[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 and return false to 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 is updated, otherwise false return rowsaffected = = 1; }
Once this method is complete, we can create a ASP.net page that allows you to edit these four product fields. Open the Errorhandling.aspx page in the EditInsertDelete folder and add a GridView control to the page through the designer. Bind this GridView to a new ObjectDataSource control, map the Select () method to the GetProducts () method of the Productsbll class, Method Update () Maps to the UpdateProduct overload that you just created.
Figure 1: Using the UpdateProduct method overload, it accepts four input parameters
This creates a ObjectDataSource that contains the UpdateParameters collection of four parameters, and a GridView that contains each field of the product. The ObjectDataSource declaration marks the OldValuesParameterFormatString property assignment to Original_{0}, which throws an exception because our BLL class does not have a name named Original_ ProductID input parameters need to be passed in. Don't forget to remove these settings from the declarative syntax (or set them to the default value: {0}).
Then, reduce the bound column of the GridView, containing only the columns Productname,quantityperunit,unitprice and UnitsInStock. Arbitrarily set some of the field-level formats you think necessary (such as changing the HeaderText property).
In previous chapters we have seen how to format UnitPrice bindings as currency formats in both read-only and edit-only modes. Here we do the same. This requires setting the DataFormatString property of the bound column to {0:C}, its HTMLEncode property is false, and its Applyformatineditmode property is true, as shown in Figure 2.
Figure 2:unitprice bound column is configured to display a currency amount
To format UnitPrice as currency in the editing interface, you need to create an event handling for the RowUpdating event of the GridView, which converts a string in a currency format to decimal. Recalling the previous section, RowUpdating event processing is also used to detect and ensure that the user enters a UnitPrice value. However, we can allow users to ignore the price column in this section.
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);
}
Our GridView contains a quantityperunit bound column, but it is only used as a display and cannot be edited by the user. To achieve this, simply set the ReadOnly property of the bound column to True.
Figure 3: Setting the QuantityPerUnit binding column as read-only
Finally, check "Enable editing" from the GridView smart tag. Once you have completed these steps, the Errorhandling.aspx page will be shown in Design view in Figure 4.
Figure 4: Delete columns other than the required bound columns and enable editing
Here we show all the columns for the product, ProductName, QuantityPerUnit, UnitPrice and UnitsInStock, but only the ProductName, UnitPrice, and UnitsInStock columns can be edited.
Figure 5: Users can now easily edit the products ' Names, prices, and units in the stock field
Step two: Properly handle the DAL layer anomaly
Our editable GridView behaves best when the user enters the legitimate product ' s name, price, and units in the stock, which results in an exception when an illegal value is entered. For example, missing a ProductName value throws a NoNullAllowedException exception because the ProductName property of the Prodcutsrow class sets its AllowDBNull property to False If the database is not functioning correctly, a SqlException exception is thrown through TableAdapter when attempting to connect to the database. Without any action, these exceptions will emerge from the data access layer to the business logic layer, then to the ASP.net page, and finally to the ASP.net runtime.
Depending on how your Web application is configured and whether the application is accessed from localhost, an unhandled exception appears on a class of server error-handling pages, a detailed error report, or a user-friendly web page. View the Web application Error handling in asp.net and customErrors Element For more information about how the ASP.net page responds to an unhandled exception.
Figure 6 shows the status of the screen when an attempt is made to update a product without specifying a ProductName value. This shows the default verbose error report when accessed through localhost.
Figure 6: Omitting product ' s name will show exception details
Although such exception details are useful when we test the application, an end-user is at a loss when faced with such an anomaly. An end user probably doesn't know what nonullallowedexception is, or how it's caused. A better approach is to present a more user-friendly message to the user that there is a problem trying to update the product.
If an exception occurs while performing this operation, the post-level events for the ObjectDataSource and data Web controls provide a way to discover that it does not appear in the ASP.net runtime. In our example, let's create an event handler for the RowUpdated event of the GridView that determines whether an exception is fired, and if so, displays exception details in a label server control.
First, add a label control to the ASP.net page, set its ID property to Exceptiondetails and empty its Text property. To attract the user's implementation to this information, set its cssclass to warning, which is a CSS category that we added to the Styles.css file in the previous chapters. Remember this CSS category lets label text appear as a large, red, italic, bold font.
Figure 7: Adding a Label server control to the page
Because we want this label control to appear only when an exception occurs, set its Visible property to false in Page_Load event handling:
protected void Page_Load (object sender, EventArgs e)
{
exceptiondetails.visible = false;
}
With this code, the Visible property of the Exceptiondetails control is set to false the first time the page is accessed and the subsequent postback. When an DAL/BLL layer exception is detected in the RowUpdated event handler of the GridView, we set the Visible property of the Exceptiondetails control to True. Because the event handling of the Web server control appears in the page lifecycle after Page_Load event processing, the label will be displayed. However, the next postback, Page_Load event handling will reset the Visible property back to false and hide it again.
Note: Alternatively, we may not have to set the Visible property of the Exceptiondetails control in Page_Load as another option, You can set its Visible property to false in the declaration syntax and disable view state (set its EnableViewState property to false). We will use this approach in future chapters.
By adding this label control, our next step is to add an event handler for the RowUpdated event of the GridView. Select the GridView control in Design view, open the Properties window, and click on the Yellow Lightning Flash icon to list all the events for the GridView. In the RowUpdating event of the GridView we can see that there is already a portal, because we have already created an event handler for this event earlier in this section. Create an event handler for the RowUpdated event.
Figure 8: Create an event handler for the GridView event
Note: You can also create this event handling from the Drop-down list at the top of the code-behind file. Select the GridView control from the Drop-down list on the left and select the RowUpdated event from the Drop-down list on the right.
Creating this event handler adds the following code to the code-behind class of the ASP.net page:
protected void gridview1_rowupdated (object sender, GridViewUpdatedEventArgs e)
{
}
The second input parameter of this event handler is an object of type GridViewUpdatedEventArgs, which has three properties about handling exceptions:
· Exception– gets the exception thrown during the update operation, or NULL if no exception is thrown
· Exceptionhandled– Gets or sets a value that indicates whether the exception that was thrown during the update operation was handled in the RowUpdated event handler, and if set to False (the default), the exception is raised again, leaking out to the ASP.net runtime
· keepineditmode– if set to true,gridview the current edit line will remain in edit mode; if set to False (default), the current row reverts to read-only mode
Then our code should detect whether exception is NULL or NOT NULL, which means that an exception is thrown when performing this operation. If so, we would like to:
• Display a user-friendly message in the Exceptiondetails control
• Indicates that the exception has been processed
• Keep the current line in edit mode
The following code implements the above purposes:
protected void gridview1_rowupdated (object sender, GridViewUpdatedEventArgs e) {if (e.exception!= null) {//Di
Splay 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 Experie
ncing problems. "+" Please try again later. "; else if (inner is nonullallowedexception) Exceptiondetails.text = "There are one or more required field
s 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 = true;
Keep the row in edit mode E.keepineditmode = true;
}
}
In this event handler, the first detection of E. Whether the exception is null. If not, set the Exceptiondetails control's Visible property to True, and set its Text property to "There was a problem updating the product." The exception details that are currently thrown are saved in E. The InnerException attribute of the exception object. Check this inner exception, and if it is a specific type, attach some additional useful information to the Exceptiondetails label's Text property. Finally, the exceptionhandled and Keepineditmode properties are set to true.
Figure 9 shows the screenshot of the page when the product name is missing, and figure 10 shows the result of entering an illegal UnitPrice value (-50).
Figure 9:productname bound column must contain a value
Figure 10:unitprice Value does not accept negative numbers
By setting the property to, the event handler indicates that the exception has been processed. Therefore, this exception is not routed to the ASP.net runtime.
Note: Figures 9 and 10 show an appropriate way to handle an exception that is thrown by incorrect user input. Ideally, however, these incorrect inputs should not reach the business logic layer, because the ASP.net page should ensure that the user's input is valid before calling the UpdateProduct method of the Productsbll class. In the next section, we'll look at how to add validation controls to the edit and insert interface to ensure that the data submitted to the business logic layer adheres to the business rules. The validation control can not only prevent calls to the UpdateProduct method until the user provides valid data, but also provides a more suggestive user experience for locating data entry problems.
Step three: Properly handle BLL layer anomalies
When you insert, update, or delete data, the data access layer throws an exception when you face a data-related error. The database may not be wired, a required database table field may not have a value specified, or a constraint between tables is violated. In addition to the identified data-related exceptions, the business logic layer uses exception directives to violate the business logic. In the section creating a business logic layer, as an example, we added a business rule to check the initial updateproduct overload. In particular, if the user marks a product for the stop supply, we require that the product not be the only one supplied by the supplier. If this condition is violated, a ApplicationException exception is thrown.
In this section, we add a business rule to the UpdateProduct Overload: Prohibit the UnitPrice field from being set to more than twice times the original value. To achieve this, adjust the updateproduct overload so that it can perform this check and throw a ApplicationException exception when the rule is violated. This update method is as follows:
public bool UpdateProduct (string productName, decimal UnitPrice, short? UnitsInStock, int productID) {Northwind.pro
Ductsdatatable products = Adapter.getproductbyproductid (ProductID); if (products.
Count = = 0)//No matching record found and return false to 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 is UpdatEd, otherwise false return rowsaffected = = 1;
}
Through this modification, any price more than twice times the current price is more than a ApplicationException exception is thrown. Like the exception thrown in the DAL, the ApplicationException exception thrown by this BLL can be detected and processed in the RowUpdated event handler of the GridView. In fact, the code of our existing RowUpdated event handler can correctly detect the exception and display the value of the ApplicationException message property. Figure 11 shows a screenshot when a user tries to update the price of a product "Chai" to $50.00, which is more than twice times the original price $19.95.
Figure 11: This business rule does not accept that the price increase is twice times higher than the existing price of the product
Note: Ideally our business rules should not be in the UpdateProduct method overload but in a public method. This is reserved for reader practice.
Summarize
During an insert, update, or delete operation, both the data Web control and the ObjectDataSource control contain pre-and post--level events that record the current operation. As we have seen in this section and in the previous section, when an editable GridView is used, the RowUpdating event of the GridView is triggered after the ObjectDataSource updating event, At this point the Update command is sent to the implied object of the ObjectDataSource. This is done, triggering the ObjectDataSource updated event after the RowUpdated event of the GridView.
We can create an event handler for these events that occur before the operation, in order to customize the input parameters;
Event-creation event handling after an action is intended to detect and the results of the corresponding operation. Post-level event handlers are often used to detect whether an exception occurred during the operation. When confronted with an exception, these post-level event handlers are free to handle the exception. In this section we have seen how to handle an exception that displays a friendly error message.
In the next section, we'll look at how to reduce the likelihood of an exception due to problems with data format (for example, entering a negative number in UnitPrice). In particular, we'll look at how to add the validation control to the edit and insert interface.
I wish you a happy programming!
Author Introduction
Scott Mitchell, with six asp/asp. NET book, is the founder of 4GuysFromRolla.com, has been applying Microsoft Web technology since 1998. Scott is an independent technical consultant, trainer, writer, recently completed a new book to be published by Sams Press, proficient in asp.net 2.0 within 24 hours. His contact email is mitchell@4guysfromrolla.com, or he can contact him through his blog Http://ScottOnWriting.NET.