Controlar las excepciones de nivel BLL y DAL (VB)

por Scott Mitchell

Descargar PDF

En este tutorial, verá cómo controlar correctamente las excepciones generadas durante un flujo de trabajo de actualización de DataList editable.

Introducción

En el tutorial Introducción a la edición y eliminación de datos en el control DataList, ha creado un control DataList que ofrecía funcionalidades de edición y eliminación sencillas. Aunque era totalmente funcional, no era fácil de usar, ya que cualquier error que se producía durante el proceso de edición o eliminación iniciaba una excepción no controlada. Por ejemplo, se inicia una excepción si se omite el nombre del producto o se escribe un valor de precio de Muy asequible al editar un producto. Como esta excepción no se detecta en el código, se propaga hasta el runtime de ASP.NET, que después, muestra los detalles de la excepción en la página web.

Como ha visto en el tutorial Control de excepciones de nivel BLL y DAL en una página ASP.NET, si se inicia una excepción desde las profundidades de las capas de lógica de negocios o acceso a datos, los detalles de la excepción se devuelven a ObjectDataSource y, después, al control GridView. Ha visto cómo controlar correctamente estas excepciones mediante la creación de controladores de eventos Updated o RowUpdated para ObjectDataSource o GridView, la comprobación de una excepción y, luego, si se indica que la excepción se ha controlado.

Pero en los tutoriales sobre DataList no se usa ObjectDataSource para actualizar y eliminar datos. En su lugar, se trabaja directamente en BLL. Para detectar excepciones que se originan en BLL o DAL, es necesario implementar código de control de excepciones en el código subyacente de la página ASP.NET. En este tutorial, verá cómo controlar más correctamente las excepciones generadas durante un flujo de trabajo de actualización de DataList editable.

Nota:

En el tutorial Información general sobre la edición y eliminación de datos en el control DataList se describen diferentes técnicas para editar y eliminar datos de DataList, y algunas técnicas implicadas en el uso de ObjectDataSource para actualizar y eliminar. Si usa estas técnicas, puede controlar las excepciones de BLL o DAL mediante los controladores de eventos Updated o Deleted de ObjectDataSource.

Paso 1: Creación de un control DataList editable

Antes de preocuparse por controlar las excepciones que se producen durante el flujo de trabajo de actualización, creará un control DataList. Abra la página ErrorHandling.aspx en la carpeta EditDeleteDataList, agregue un control DataList al Diseñador, establezca su propiedad ID en Productsy agregue una instancia de ObjectDataSource denominada ProductsDataSource. Configure ObjectDataSource para usar el método GetProducts() de la clase ProductsBLL para seleccionar registros; establezca las listas desplegables de las pestañas INSERT, UPDATE y DELETE en (None).

Return the Product Information Using the GetProducts() Method

Figura 1: Recuperación de información del producto mediante el método GetProducts() (Haga clic para ver la imagen a tamaño completo)

Después de completar el asistente para ObjectDataSource, Visual Studio creará automáticamente una instancia de ItemTemplate para DataList. Reemplácelo por una instancia de ItemTemplate que muestra el nombre y el precio de cada producto, e incluya un botón Editar. A continuación, cree una instancia de EditItemTemplate con un control web TextBox para los botones Nombre y Precio y Actualizar y Cancelar. Por último, establezca la propiedad RepeatColumns de DataList en 2.

Después de realizar estos cambios, el marcado declarativo de la página debe ser similar al siguiente. Compruebe que los botones Editar, Cancelar y Actualizar tienen sus propiedades CommandName establecidas en Editar, Cancelar y Actualizar, respectivamente.

<asp:DataList ID="Products" runat="server" DataKeyField="ProductID"
    DataSourceID="ProductsDataSource" RepeatColumns="2">
    <ItemTemplate>
        <h5>
            <asp:Label runat="server" ID="ProductNameLabel"
                Text='<%# Eval("ProductName") %>' />
        </h5>
        Price:
            <asp:Label runat="server" ID="Label1"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
            <asp:Button runat="server" id="EditProduct" CommandName="Edit"
                Text="Edit" />
        <br />
        <br />
    </ItemTemplate>
    <EditItemTemplate>
        Product name:
            <asp:TextBox ID="ProductName" runat="server"
                Text='<%# Eval("ProductName") %>' />
        <br />
        Price:
            <asp:TextBox ID="UnitPrice" runat="server"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
        <br />
            <asp:Button ID="UpdateProduct" runat="server" CommandName="Update"
                Text="Update" /> 
            <asp:Button ID="CancelUpdate" runat="server" CommandName="Cancel"
                Text="Cancel" />
    </EditItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
    SelectMethod="GetProducts" TypeName="ProductsBLL"
    OldValuesParameterFormatString="original_{0}">
</asp:ObjectDataSource>

Nota:

Para este tutorial, el estado de visualización de DataList debe estar habilitado.

Tómese un momento para ver el progreso en un explorador (vea la figura 2).

Each Product Includes an Edit Button

Figura 2: Cada producto incluye un botón Editar (Haga clic para ver la imagen a tamaño completo)

Actualmente, el botón Editar solo genera un postback y todavía no permite que se pueda editar el producto. Para habilitar la edición, es necesario crear controladores de eventos para los eventos EditCommand, CancelCommand y UpdateCommand de DataList. Los eventos EditCommand y CancelCommand simplemente actualizan la propiedad EditItemIndex de DataList y vuelven a enlazar los datos al control:

Protected Sub Products_EditCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.EditCommand
    ' Set the DataList's EditItemIndex property to the
    ' index of the DataListItem that was clicked
    Products.EditItemIndex = e.Item.ItemIndex
    ' Rebind the data to the DataList
    Products.DataBind()
End Sub
Protected Sub Products_CancelCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.CancelCommand
    ' Set the DataList's EditItemIndex property to -1
    Products.EditItemIndex = -1
    ' Rebind the data to the DataList
    Products.DataBind()
End Sub

El controlador de eventos UpdateCommand es un poco más complejo. Debe leer el valor ProductID de la colección DataKeys del producto editado junto con el nombre del producto y el precio de los cuadros de texto en EditItemTemplate y, después, llamar al método UpdateProduct de la clase ProductsBLL antes de devolver el control DataList a su estado previo a la edición.

Por ahora, se usará exactamente el mismo código del controlador de eventos UpdateCommand del tutorial Introducción a la edición y eliminación de datos en el control DataList. En el paso 2 se agregará el código para controlar correctamente las excepciones.

Protected Sub Products_UpdateCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.UpdateCommand
    ' Read in the ProductID from the DataKeys collection
    Dim productID As Integer = Convert.ToInt32(Products.DataKeys(e.Item.ItemIndex))
    ' Read in the product name and price values
    Dim productName As TextBox = CType(e.Item.FindControl("ProductName"), TextBox)
    Dim unitPrice As TextBox = CType(e.Item.FindControl("UnitPrice"), TextBox)
    Dim productNameValue As String = Nothing
    If productName.Text.Trim().Length > 0 Then
        productNameValue = productName.Text.Trim()
    End If
    Dim unitPriceValue As Nullable(Of Decimal) = Nothing
    If unitPrice.Text.Trim().Length > 0 Then
        unitPriceValue = Decimal.Parse(unitPrice.Text.Trim(), _
                         System.Globalization.NumberStyles.Currency)
    End If
    ' Call the ProductsBLL's UpdateProduct method...
    Dim productsAPI As New ProductsBLL()
    productsAPI.UpdateProduct(productNameValue, unitPriceValue, productID)
    ' Revert the DataList back to its pre-editing state
    Products.EditItemIndex = -1
    Products.DataBind()
End Sub

En el caso de una entrada no válida que puede tener forma de un precio unitario con formato incorrecto, un valor de precio unitario no válido como -5 USD o la omisión del nombre del producto, se generará una excepción. Como el controlador de eventos UpdateCommand no incluye ningún código de control de excepciones en este momento, la excepción se propagará hasta el runtime de ASP.NET, donde se mostrará al usuario final (vea la figura 3).

When an Unhandled Exception Occurs, the End User Sees an Error Page

Figura 3: Cuando se produce una excepción no controlada, el usuario final ve una página de error

Paso 2: Control correcto de excepciones en el controlador de eventos UpdateCommand

Durante el flujo de trabajo de actualización, se pueden producir excepciones en el controlador de eventos UpdateCommand, BLL o DAL. Por ejemplo, si un usuario escribe un precio demasiado caro, la instrucción Decimal.Parse del controlador de eventos UpdateCommand iniciará una excepción FormatException. Si el usuario omite el nombre del producto o si el precio tiene un valor negativo, en DAL se generará una excepción.

Cuando se produce una excepción, querrá mostrar un mensaje informativo dentro de la propia página. Agregue un control web Label a la página cuyo valor ID se establezca en ExceptionDetails. Configure el texto de la etiqueta para que se muestre en una fuente roja, extra grande, en negrita y cursiva; para ello, asigne su propiedad CssClass a la clase CSS Warning, que se define en el archivo Styles.css.

Cuando se produce un error, solo querrá que la etiqueta se muestre una vez. Es decir, en posteriores postbacks, el mensaje de advertencia de la etiqueta debe desaparecer. Esto se puede lograr si borra la propiedad Text de Label o establece su propiedad Visible en False en el controlador de eventos Page_Load (como en el tutorial Control de excepciones de nivel BLL y DAL en una página ASP.NET), o bien deshabilita la compatibilidad con el estado de visualización de etiqueta. Se usará la última opción.

<asp:Label ID="ExceptionDetails" EnableViewState="False" CssClass="Warning"
    runat="server" />

Cuando se produzca una excepción, sus detalles se asignarán la excepción a la propiedad Text del control Label ExceptionDetails. Como su estado de visualización está deshabilitado, en postbacks posteriores se perderán los cambios mediante programación de la propiedad Text, se volverá al texto predeterminado (una cadena vacía) y, por tanto, se ocultará el mensaje de advertencia.

Para determinar cuándo se ha generado un error para mostrar un mensaje útil en la página, es necesario agregar un bloque Try ... Catch al controlador de eventos UpdateCommand. La parte Try contiene código que puede provocar una excepción, mientras que el bloque Catch contiene código que se ejecuta en caso de una excepción. Consulte la sección Aspectos básicos del control de excepciones en la documentación de .NET Framework para más información sobre el bloque Try ... Catch.

Protected Sub Products_UpdateCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.UpdateCommand
    ' Handle any exceptions raised during the editing process
    Try
        ' Read in the ProductID from the DataKeys collection
        Dim productID As Integer = _
            Convert.ToInt32(Products.DataKeys(e.Item.ItemIndex))
        ... Some code omitted for brevity ...
    Catch ex As Exception
        ' TODO: Display information about the exception in ExceptionDetails
    End Try
End Sub

Cuando el código dentro del bloque Try inicia una excepción de cualquier tipo, comenzará a ejecutarse el código del bloque Catch. El tipo de excepción que se inicia DbException, NoNullAllowedException, ArgumentException, etc., depende de lo que exactamente haya precipitado el error en primer lugar. Si hay un problema en el nivel de base de datos, se iniciará una excepción DbException. Si se ha escrito un valor no válido para los campos UnitPrice, UnitsInStock, UnitsOnOrder o ReorderLevel, se iniciará una excepción ArgumentException, ya que se ha agregado código para validar estos valores de campo en la clase ProductsDataTable (vea el tutorial Creación de una capa lógica de negocios).

Se puede proporcionar una explicación más útil al usuario final si el texto del mensaje se basa en el tipo de excepción detectado. En el código siguiente, que se ha usado casi de forma idéntica en el tutorial Control de excepciones de nivel de BLL y DAL en una página ASP.NET, se proporciona este nivel de detalle:

Private Sub DisplayExceptionDetails(ByVal ex As Exception)
    ' Display a user-friendly message
    ExceptionDetails.Text = "There was a problem updating the product. "
    If TypeOf ex Is System.Data.Common.DbException Then
        ExceptionDetails.Text += "Our database is currently experiencing problems." + _
                                 "Please try again later."
    ElseIf TypeOf ex Is System.Data.NoNullAllowedException Then
        ExceptionDetails.Text+="There are one or more required fields that are missing."
    ElseIf TypeOf ex Is ArgumentException Then
        Dim paramName As String = CType(ex, ArgumentException).ParamName
        ExceptionDetails.Text+=String.Concat("The ", paramName, " value is illegal.")
    ElseIf TypeOf ex Is ApplicationException Then
        ExceptionDetails.Text += ex.Message
    End If
End Sub

Para completar este tutorial, simplemente llame al método DisplayExceptionDetails desde el bloque Catch y pase la instancia Exception detectada (ex).

Con el bloque Try ... Catch implementado, se presenta a los usuarios un mensaje de error más informativo, como se muestra en las figuras 4 y 5. Tenga en cuenta que, ante una excepción, el control DataList permanece en modo de edición. Esto se debe a que una vez que se produce la excepción, el flujo de control se redirige inmediatamente al bloque Catch y se ignora el código que devuelve DataList a su estado previo a la edición.

An Error Message is Displayed if a User Omits a Required Field

Figura 4: Se muestra un mensaje de error si un usuario omite un campo obligatorio (Haga clic para ver la imagen a tamaño completo)

An Error Message is Displayed When Entering a Negative Price

Figura 5: Se muestra un mensaje de error al escribir un precio negativo (Haga clic para ver la imagen a tamaño completo)

Resumen

GridView y ObjectDataSource proporcionan controladores de eventos de nivel posterior que incluyen información sobre las excepciones que se hayan generado durante el flujo de trabajo de actualización y eliminación, así como las propiedades que se pueden establecer para indicar si se ha controlado o no la excepción. Pero estas características no están disponibles cuando se trabaja con DataList y se usa BLL directamente. En su lugar, es responsable de implementar el control de excepciones.

En este tutorial ha visto cómo agregar control de excepciones a un flujo de trabajo de actualización de un control DataList editable mediante la adición de un bloque Try ... Catch al controlador de eventos UpdateCommand. Si se produce una excepción durante el flujo de trabajo de actualización, se ejecuta el código del bloque Catch y se muestra información útil en la el control Label ExceptionDetails.

En este momento, DataList no hace ningún esfuerzo para evitar que se produzcan excepciones. Aunque se sabe que un precio negativo dará lugar a una excepción, aún se no ha agregado ninguna funcionalidad para evitar que un usuario escriba una entrada de ese tipo. En el siguiente tutorial, verá cómo reducir las excepciones generadas por una entrada de usuario no válida mediante la adición de controles de validación en EditItemTemplate.

¡Feliz programación!

Lecturas adicionales

Para más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:

Acerca del autor

Scott Mitchell, autor de siete libros de ASP y ASP.NET, y fundador de 4GuysFromRolla.com, trabaja con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.

Agradecimientos especiales a

Esta serie de tutoriales fue revisada por muchos revisores. El revisor principal de este tutorial fue Ken Pespisa. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.