Compartir a través de


Controlar las excepciones de nivel BLL y DAL en una página de ASP.NET (VB)

de Scott Mitchell

Descargar PDF

En este tutorial veremos cómo mostrar un mensaje de error descriptivo e informativo si se produce una excepción durante una operación de inserción, actualización o eliminación de un control web de datos ASP.NET.

Introducción

Trabajar con datos de una aplicación web de ASP.NET mediante una arquitectura de aplicación en capas implica los tres pasos generales siguientes:

  1. Determina qué método de la capa de lógica empresarial debe invocarse y qué parámetros deben pasarse. Los valores de parámetro pueden codificarse de forma rígida, asignarse mediante programación o entradas introducidas por el usuario.
  2. Invoque el método.
  3. Procese los resultados. Al llamar a un método BLL que devuelve datos, esto puede implicar el enlace de los datos a un control web de datos. En el caso de los métodos BLL que modifican los datos, esto puede incluir realizar alguna acción basada en un valor devuelto o controlar correctamente cualquier excepción que surgió en el paso 2.

Como vimos en el tutorial anterior, los controles ObjectDataSource y Data Web proporcionan puntos de extensibilidad para los pasos 1 y 3. GridView, por ejemplo, desencadena su RowUpdating evento antes de asignar sus valores de campo a su colección ObjectDataSource UpdateParameters ; su RowUpdated evento se genera después de que ObjectDataSource haya completado la operación.

Ya hemos examinado los eventos que se activan durante el paso 1 y hemos visto cómo se pueden usar para personalizar los parámetros de entrada o cancelar la operación. En este tutorial, prestaremos atención a los eventos que ocurren después de que se haya completado la operación. Con estos controladores de eventos posteriores, podemos, entre otras cosas, determinar si se produjo una excepción durante la operación y manejarlo adecuadamente, mostrando un mensaje de error amistoso e informativo en la pantalla en lugar de utilizar la página de excepción estándar de ASP.NET.

Para ilustrar cómo trabajar con estos eventos posteriores al nivel, vamos a crear una página que muestre los productos en una gridView editable. Al actualizar un producto, si se produce una excepción en nuestra página de ASP.NET se mostrará un mensaje corto encima de GridView que explica que se ha producido un problema. ¡Comencemos!

Paso 1: Crear un GridView editable de productos

En el tutorial anterior hemos creado una gridView editable con solo dos campos y ProductNameUnitPrice. Esto requería crear una sobrecarga adicional para el método de ProductsBLL la UpdateProduct clase, una que solo aceptaba tres parámetros de entrada (el nombre del producto, el precio unitario y el identificador) en lugar de un parámetro para cada campo de producto. Para este tutorial, vamos a volver a practicar esta técnica, creando una gridView editable que muestre el nombre del producto, la cantidad por unidad, el precio unitario y las unidades en existencias, pero solo permite editar el nombre, el precio unitario y las unidades en existencias.

Para dar cabida a este escenario, necesitaremos otra sobrecarga del UpdateProduct método, una que acepte cuatro parámetros: el nombre del producto, el precio unitario, las unidades en existencias y el identificador. Agregue el siguiente método a la clase ProductsBLL:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct _
    (ByVal productName As String, ByVal unitPrice As Nullable(Of Decimal), _
ByVal unitsInStock As Nullable(Of Short), ByVal productID As Integer) As Boolean
    Dim products As Northwind.ProductsDataTable = _
        Adapter.GetProductByProductID(productID)
    If products.Count = 0 Then
        Return False
    End If
    Dim product As Northwind.ProductsRow = products(0)
    product.ProductName = productName
    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If
    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If
    Dim rowsAffected As Integer = Adapter.Update(product)
    Return rowsAffected = 1
End Function

Con este método completo, estamos listos para crear la página de ASP.NET que permite editar estos cuatro campos de producto concretos. Abra la ErrorHandling.aspx página en la EditInsertDelete carpeta y agregue gridView a la página a través del Diseñador. Vincula GridView a un nuevo ObjectDataSource, asignando el método Select() al método ProductsBLL de la clase GetProducts() y el método Update() a la sobrecarga UpdateProduct recién creada.

Usar la sobrecarga del método UpdateProduct que acepta cuatro parámetros de entrada

Figura 1: Utilice la sobrecarga del método que acepta cuatro parámetros de entrada (UpdateProduct)

Esto creará un ObjectDataSource con una UpdateParameters colección con cuatro parámetros y gridView con un campo para cada uno de los campos de producto. El marcado declarativo de ObjectDataSource asigna el valor OldValuesParameterFormatString a la propiedad original_{0}, lo que provocará una excepción, ya que la clase BLL no espera que se le pase un parámetro de entrada llamado original_productID. No olvide quitar esta configuración por completo de la sintaxis declarativa (o establézcala en el valor predeterminado, {0}).

A continuación, reduzca el GridView para incluir solo los ProductName, QuantityPerUnit, UnitPrice y UnitsInStock BoundFields. También puede aplicar cualquier formato de nivel de campo que considere necesario (por ejemplo, cambiar las HeaderText propiedades).

En el tutorial anterior hemos visto cómo dar formato UnitPrice a BoundField como moneda tanto en modo de solo lectura como en modo de edición. Hagamos lo mismo aquí. Recuerde que esta configuración requería establecer la propiedad BoundField DataFormatString en {0:c}, su propiedad HtmlEncode a false y su ApplyFormatInEditMode a true, como se muestra en la Figura 2.

Configurar UnitPrice BoundField para mostrar como moneda

Figura 2: Configurar boundField UnitPrice para mostrar como moneda (haga clic para ver la imagen de tamaño completo)

Dar formato a UnitPrice como moneda en la interfaz de edición requiere crear un controlador de eventos para el evento RowUpdating del GridView que convierta la cadena en formato de moneda en un valor de decimal. Recuerde que el RowUpdating controlador de eventos del último tutorial también verificó asegurar que el usuario proporcionara un UnitPrice valor. Sin embargo, para este tutorial vamos a permitir que el usuario omita el precio.

Protected Sub GridView1_RowUpdating(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) _
    Handles GridView1.RowUpdating
    If e.NewValues("UnitPrice") IsNot Nothing Then
        e.NewValues("UnitPrice") = _
            Decimal.Parse(e.NewValues("UnitPrice").ToString(), _
            System.Globalization.NumberStyles.Currency)
    End If

GridView incluye un QuantityPerUnit BoundField, pero este BoundField solo debe ser para fines de visualización y no debe ser editable por el usuario. Para organizar esto, simplemente establezca la propiedad BoundFields en ReadOnlytrue.

Hacer que QuantityPerUnit BoundField sea de solo lectura

Figura 3: Hacer el BoundField Read-Only (QuantityPerUnit de tamaño completo)

Por último, active la casilla Habilitar edición en la etiqueta inteligente de GridView. Después de completar estos pasos, el diseñador de la página debería parecerse a la figura 4 que se muestra en ErrorHandling.aspx.

Eliminar todos los campos vinculados excepto los necesarios y marcar la casilla Habilitar edición

Figura 4: Quitar todos los campos delimitados necesarios y comprobar la casilla Habilitar edición (haga clic para ver la imagen de tamaño completo)

En este punto, tenemos una lista de todos los campos ProductName, QuantityPerUnit, UnitPrice y UnitsInStock de los productos; sin embargo, solo se pueden editar los campos ProductName, UnitPrice y UnitsInStock.

Los usuarios ahora pueden editar fácilmente los nombres, los precios y las unidades de los productos en los campos stock

Figura 5: Los usuarios ahora pueden editar fácilmente los nombres, los precios y las unidades de productos en campos de existencias (haga clic para ver la imagen de tamaño completo)

Paso 2: Control correcto de excepciones de DAL-Level

Mientras que el GridView editable funciona maravillosamente cuando los usuarios ingresan valores válidos para el nombre, el precio y las unidades disponibles del producto editado, ingresar valores ilegales da como resultado una excepción. Por ejemplo, si se omite el valor ProductName, se produce una NoNullAllowedException, ya que la propiedad ProductName en la clase ProductsRow tiene su propiedad AllowDBNull establecida en false; si la base de datos está inactiva, TableAdapter lanzará una SqlException al intentar conectarse a la base de datos. Sin realizar ninguna acción, estas excepciones se propagan desde la capa de acceso a datos hasta la capa lógica de negocios, a continuación, a la página de ASP.NET y, por último, al entorno de ejecución de ASP.NET.

Dependiendo de cómo se configure la aplicación web y de si está visitando la aplicación desde localhost, una excepción no controlada puede dar lugar a una página genérica de error de servidor, un informe de errores detallado o una página web fácil de usar. Consulte Control de errores de aplicación web en ASP.NET y el elemento customErrors para obtener más información sobre cómo responde el entorno de ejecución de ASP.NET a una excepción no detectada.

En la figura 6 se muestra la pantalla encontrada al intentar actualizar un producto sin especificar el ProductName valor. Este es el informe de errores detallado predeterminado que se muestra al pasar por localhost.

Si se omite el nombre del producto, se mostrarán los detalles de la excepción.

Figura 6: Omitir el nombre del producto mostrará los detalles de excepción (haga clic para ver la imagen de tamaño completo)

Aunque estos detalles de excepción son útiles al probar una aplicación, presentar a un usuario final con tal pantalla frente a una excepción es menor que lo ideal. Es probable que un usuario final no sepa qué es un NoNullAllowedException o por qué lo causó. Un mejor enfoque es presentar al usuario un mensaje más fácil de usar que explica que había problemas al intentar actualizar el producto.

Si se produce una excepción al realizar la operación, los eventos post-nivel tanto en ObjectDataSource como en el control web de datos proporcionan un medio para detectarlo y evitar que la excepción se propague hasta el entorno de ejecución de ASP.NET. En nuestro ejemplo, vamos a crear un controlador de eventos para el evento de RowUpdated GridView que determina si se ha desencadenado una excepción y, si es así, muestra los detalles de la excepción en un control Web de etiqueta.

Empiece agregando una etiqueta a la página ASP.NET, estableciendo su ID propiedad en ExceptionDetails y borrando su Text propiedad. Para atraer la atención del usuario a este mensaje, establezca su propiedad CssClass a Warning, que es una clase CSS que hemos agregado en el archivo Styles.css en el tutorial anterior. Recuerde que esta clase CSS hace que el texto de la etiqueta se muestre en una fuente roja, cursiva, negrita y extra grande.

Agregar un control web de etiqueta a la página

Figura 7: Agregar un control web etiqueta a la página (haga clic para ver la imagen de tamaño completo)

Puesto que queremos que este control Web de etiqueta sea visible solo inmediatamente después de que se haya producido una excepción, establezca su Visible propiedad en false en el controlador de Page_Load eventos:

Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    ExceptionDetails.Visible = False
End Sub

Con este código, en la primera visita a la página y en los postbacks posteriores, el ExceptionDetails control tendrá su Visible propiedad establecida en false. Ante una excepción de nivel DAL o BLL, que podemos detectar en el controlador de eventos de RowUpdated del GridView, estableceremos la propiedad ExceptionDetails del control Visible a verdadero. Dado que los controladores de eventos de control web se producen después del Page_Load controlador de eventos en el ciclo de vida de la página, se mostrará la etiqueta. Sin embargo, en el siguiente postback, el Page_Load controlador de eventos revertirá la Visible propiedad a false, y lo ocultará de nuevo de la vista.

Nota:

Como alternativa, podríamos quitar la necesidad de establecer la propiedad del control ExceptionDetails en Visible al asignar su propiedad Page_Load a Visible en la sintaxis declarativa y deshabilitando su estado de vista configurando su propiedad false a EnableViewState. Usaremos este enfoque alternativo en un tutorial futuro.

Con el control Label agregado, el siguiente paso es crear el controlador de eventos para el evento de RowUpdated GridView. Seleccione GridView en el Diseñador, vaya a la ventana Propiedades y haga clic en el icono de rayo, enumerando los eventos de GridView. Ya debería haber una entrada para el evento de RowUpdating GridView, ya que creamos un controlador de eventos para este evento anteriormente en este tutorial. Cree también un controlador de eventos para el RowUpdated evento.

Crear un controlador de eventos para el evento RowUpdated de GridView

Figura 8: Crear un controlador de eventos para el evento de RowUpdated GridView

Nota:

También puede crear el controlador de eventos a través de las listas desplegables en la parte superior del archivo de clase de código subyacente. Seleccione GridView en la lista desplegable de la izquierda y el evento RowUpdated de la lista de la derecha.

Al crear este controlador de eventos, se agregará el código siguiente a la clase de código subyacente de la página de ASP.NET:

Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
    Handles GridView1.RowUpdated
End Sub

El segundo parámetro de entrada de este controlador de eventos es un objeto de tipo GridViewUpdatedEventArgs, que tiene tres propiedades de interés para controlar excepciones:

  • Exception una referencia a la excepción lanzada; si no se ha producido ninguna excepción, esta propiedad tendrá un valor de . null
  • ExceptionHandled un valor booleano que indica si la excepción se controló o no en el RowUpdated controlador de eventos; si false (el valor predeterminado), la excepción se vuelve a lanzar, propagándose hasta el entorno de ejecución de ASP.NET.
  • KeepInEditMode si se establece en true la fila GridView editada permanece en modo de edición; si false (el valor predeterminado), la fila GridView vuelve a su modo de solo lectura.

Nuestro código debe comprobar, a continuación, si Exception no es igual a null, lo que significa que se generó una excepción al realizar la operación. Si este es el caso, queremos:

  • Mostrar un mensaje descriptivo en la ExceptionDetails etiqueta
  • Indica que se controló la excepción
  • Mantener la fila GridView en modo de edición

Este código siguiente logra estos objetivos:

Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
    Handles GridView1.RowUpdated
    If e.Exception IsNot Nothing Then
        ExceptionDetails.Visible = True
        ExceptionDetails.Text = "There was a problem updating the product. "
        If e.Exception.InnerException IsNot Nothing Then
            Dim inner As Exception = e.Exception.InnerException
            If TypeOf inner Is System.Data.Common.DbException Then
                ExceptionDetails.Text &= _
                "Our database is currently experiencing problems." & _
                "Please try again later."
            ElseIf TypeOf inner _
             Is System.Data.NoNullAllowedException Then
                ExceptionDetails.Text += _
                    "There are one or more required fields that are missing."
            ElseIf TypeOf inner Is ArgumentException Then
                Dim paramName As String = CType(inner, ArgumentException).ParamName
                ExceptionDetails.Text &= _
                    String.Concat("The ", paramName, " value is illegal.")
            ElseIf TypeOf inner Is ApplicationException Then
                ExceptionDetails.Text += inner.Message
            End If
        End If
        e.ExceptionHandled = True
        e.KeepInEditMode = True
    End If
End Sub

Este controlador de eventos comienza comprobando si e.Exception es null. Si no es así, la propiedad ExceptionDetails del Visible Label se establece en true y su propiedad Text en "Hubo un problema al actualizar el producto". Los detalles de la excepción real que se produjo residen en la propiedad e.Exception del objeto InnerException. Esta excepción interna se examina y, si es de un tipo determinado, se anexa un mensaje adicional útil a la ExceptionDetails propiedad Label Text . Por último, las propiedades ExceptionHandled y KeepInEditMode se establecen en true.

La figura 9 muestra una captura de pantalla de esta página al omitir el nombre del producto; En la figura 10 se muestran los resultados al escribir un valor no válido UnitPrice (-50).

ProductName BoundField debe contener un valor

Figura 9: BoundField ProductName debe contener un valor (haga clic para ver la imagen de tamaño completo)

No se permiten valores UnitPrice negativos

Figura 10: No se permiten valores negativos UnitPrice (haga clic para ver la imagen de tamaño completo)

Al establecer la propiedad e.ExceptionHandled a true, el controlador de eventos RowUpdated ha indicado que ha gestionado la excepción. Por lo tanto, la excepción no se propagará hasta el entorno de ejecución de ASP.NET.

Nota:

Las figuras 9 y 10 muestran una manera correcta de controlar las excepciones generadas debido a una entrada de usuario no válida. Lo ideal sería que tal entrada no válida nunca llegara a la capa lógica de negocio en primer lugar, ya que la página ASP.NET debería asegurarse de que las entradas del usuario sean válidas antes de invocar el método ProductsBLL de la clase UpdateProduct. En nuestro siguiente tutorial veremos cómo agregar controles de validación a las interfaces de edición e inserción para asegurarse de que los datos enviados a la capa de lógica de negocios se ajustan a las reglas de negocio. Los controles de validación no solo impiden la invocación del UpdateProduct método hasta que los datos proporcionados por el usuario sean válidos, sino que también proporcionan una experiencia de usuario más informativa para identificar problemas de entrada de datos.

Paso 3: Manejo adecuado de excepciones de BLL-Level

Al insertar, actualizar o eliminar datos, la capa de acceso a datos puede producir una excepción ante un error relacionado con los datos. La base de datos puede estar sin conexión, es posible que una columna de tabla de base de datos necesaria no haya tenido un valor especificado o que se haya infringido una restricción de nivel de tabla. Además de excepciones estrictamente relacionadas con los datos, la capa lógica de negocios puede usar excepciones para indicar cuándo se han infringido las reglas de negocio. En el tutorial Creación de una capa de lógica de negocios , por ejemplo, se ha agregado una comprobación de reglas de negocios a la sobrecarga original UpdateProduct . En concreto, si el usuario marcaba un producto como descontinuado, requeríamos que el producto no fuera el único proporcionado por su proveedor. Si se infringió esta condición, se lanzó un ApplicationException.

Para la UpdateProduct sobrecarga creada en este tutorial, vamos a agregar una regla empresarial que prohíba que el campo UnitPrice se establezca en un nuevo valor que sea más del doble del valor original UnitPrice. Para ello, ajuste la UpdateProduct sobrecarga para que realice esta comprobación y lance una ApplicationException si se infringe la regla. El método actualizado sigue:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
    Public Function UpdateProduct(ByVal productName As String, _
    ByVal unitPrice As Nullable(Of Decimal), ByVal unitsInStock As Nullable(Of Short), _
    ByVal productID As Integer) As Boolean
    Dim products As Northwind.ProductsDataTable = Adapter.GetProductByProductID(productID)
    If products.Count = 0 Then
        Return False
    End If
    Dim product As Northwind.ProductsRow = products(0)
    If unitPrice.HasValue AndAlso Not product.IsUnitPriceNull() Then
        If unitPrice > product.UnitPrice * 2 Then
            Throw New ApplicationException( _
                "When updating a product price," & _
                " the new price cannot exceed twice the original price.")
        End If
    End If
    product.ProductName = productName
    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If
    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If
    Dim rowsAffected As Integer = Adapter.Update(product)
    Return rowsAffected = 1
End Function

Con este cambio, cualquier actualización de precios que sea más de dos veces el precio actual provocará una ApplicationException. Al igual que la excepción generada desde la DAL, esta excepción generada por el BLL ApplicationException se puede detectar y controlar en el controlador de eventos del GridView RowUpdated. De hecho, el RowUpdated código del controlador de eventos, tal como se ha escrito, detectará correctamente esta excepción y mostrará el ApplicationExceptionvalor de propiedad de Message . La figura 11 muestra una captura de pantalla cuando un usuario intenta actualizar el precio de Chai a 50,00 USD, que es más del doble de su precio actual de 19,95 USD.

Las reglas de negocios no permiten que el precio aumente más que el doble del precio de un producto.

Figura 11: Las reglas de negocios no permiten los aumentos de precios que superan el doble del precio de un producto (haga clic para ver la imagen de tamaño completo)

Nota:

Idealmente, las reglas de negocio se refactorizarían desde las sobrecargas de métodos UpdateProduct hacia un método común. Esto se deja como un ejercicio para el lector.

Resumen

Durante las operaciones de inserción, actualización y eliminación de datos, tanto el control web de datos como el ObjectDataSource implicado activan eventos previos y posteriores que delimitan la operación real. Como vimos en este tutorial y en el anterior, cuando se trabaja con un GridView editable, se desencadena el evento RowUpdating del GridView, seguido del evento Updating del ObjectDataSource, en el que se realiza el comando de actualización en el objeto subyacente del ObjectDataSource. Una vez completada la operación, se desencadena el evento del ObjectDataSource Updated, seguido por el evento del GridView RowUpdated.

Podemos crear controladores de eventos para los eventos de nivel previo para personalizar los parámetros de entrada o para los eventos posteriores para inspeccionar y responder a los resultados de la operación. Los controladores de eventos posteriores se usan normalmente para detectar si se produjo una excepción durante la operación. En el caso de una excepción, estos controladores de eventos posteriores pueden controlar opcionalmente la excepción por su cuenta. En este tutorial hemos visto cómo manejar tal excepción mostrando un mensaje de error amistoso.

En el siguiente tutorial veremos cómo reducir la probabilidad de excepciones derivadas de problemas de formato de datos (por ejemplo, escribir un negativo UnitPrice). En concreto, veremos cómo agregar controles de validación a las interfaces de edición e inserción.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando 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 en 24 horas. Se le puede contactar en mitchell@4GuysFromRolla.com.

Agradecimientos especiales a

Esta serie de tutoriales fue revisada por muchos revisores. El revisor principal de este tutorial fue Liz Shulok. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.