Controlar las excepciones de nivel BLL y DAL (C#)

por Scott Mitchell

Descargar PDF

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

Introducción

En la tutorial Introducción a la edición y eliminación de datos en el DataList, creamos una lista de datos que ofrecía funcionalidades de edición y eliminación sencillas. Aunque era totalmente funcional, apenas era fácil de usar, ya que cualquier error que se produjo durante el proceso de edición o eliminación dio lugar a una excepción no controlada. Por ejemplo, omitir el nombre del producto o, al editar un producto, escribir un valor de precio de ¡Muy asequible!, produce una excepción. Dado que esta excepción no se detecta en el código, se propaga hasta el entorno de ejecución de ASP.NET, que a continuación muestra los detalles de la excepción en la página web.

Como vimos en el tutorial Control de excepciones de nivel BLL y DAL en una página de ASP.NET, si se genera una excepción a partir de 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 a continuación, a GridView. Hemos visto cómo controlar correctamente estas excepciones creando controlador de eventos Updated o RowUpdated para ObjectDataSource o GridView, comprobando una excepción y a continuación, indicando que se controló la excepción.

Sin embargo, nuestros tutoriales de DataList no usan ObjectDataSource para actualizar y eliminar datos. En su lugar, estamos trabajando directamente contra el 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 nuestra página de ASP.NET. En este tutorial, veremos cómo controlar de forma más táctil las excepciones generadas durante un flujo de trabajo de actualización de DataList editable.

Nota:

En el Introducción a la edición y eliminación de datos en el tutorial de DataList analizamos diferentes técnicas para editar y eliminar datos de DataList, algunas técnicas implicadas en el uso de ObjectDataSource para actualizar y eliminar. Si emplea estas técnicas, puede controlar las excepciones de BLL o DAL mediante los controladores de eventos Updated o Deleted de ObjectDataSource.

Paso 1: Crear una DataList editable

Antes de preocuparnos por controlar las excepciones que se producen durante el flujo de trabajo de actualización, primero vamos a crear una lista de DataList. Abra la ErrorHandling.aspx página en la EditDeleteDataList carpeta, agregue un objeto DataList al Diseñador, establezca su ID propiedad en Products, y agregue un nuevo ObjectDataSource denominado ProductsDataSource. Configure ObjectDataSource para usar el ProductsBLL método de la clase s GetProducts() para seleccionar registros; establezca las listas desplegables en las pestañas INSERT, UPDATE y DELETE en (Ninguno).

Return the Product Information Using the GetProducts() Method

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

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

Después de estos cambios, el marcado declarativo de la página debe tener un aspecto similar al siguiente. Compruebe que los botones Editar, Cancelar y Actualizar tienen sus CommandName propiedades 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 vista de DataList debe estar habilitado.

Dedique un momento a ver el progreso a través de un explorador (vea la figura 2).

Each Product Includes an Edit Button

Figura 2: cada producto incluye un botón de edición (hacer clic para ver la imagen de tamaño completo)

Actualmente, el botón Editar solo hace que se pueda editar un postback. Para habilitar la edición, es necesario crear controladores de eventos para los eventos DataList EditCommand, CancelCommandy UpdateCommand. ElEditCommand evento y CancelCommand simplemente actualizan la propiedad DataList y EditItemIndex vuelven a enlazar los datos a DataList:

protected void Products_EditCommand(object source, DataListCommandEventArgs e)
{
    // 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();
}
protected void Products_CancelCommand(object source, DataListCommandEventArgs e)
{
    // Set the DataList's EditItemIndex property to -1
    Products.EditItemIndex = -1;
    // Rebind the data to the DataList
    Products.DataBind();
}

El UpdateCommand controlador de eventos está un poco más implicado. Debe leer en los productos editados ProductID de la DataKeyscolección junto con el nombre y el precio del producto de los Cuadros de texto de la EditItemTemplatey, a continuación, llamar al método ProductsBLL clase s UpdateProduct antes de devolver DataList a su estado de edición previa.

Por ahora, vamos a usar exactamente el mismo código del UpdateCommand controlador de eventos en la introducción a la edición y eliminación de datos en el tutorial DataList. Agregaremos el código para controlar correctamente las excepciones en el paso 2.

protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
    // Read in the ProductID from the DataKeys collection
    int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
    // Read in the product name and price values
    TextBox productName = (TextBox)e.Item.FindControl("ProductName");
    TextBox unitPrice = (TextBox)e.Item.FindControl("UnitPrice");
    string productNameValue = null;
    if (productName.Text.Trim().Length > 0)
        productNameValue = productName.Text.Trim();
    decimal? unitPriceValue = null;
    if (unitPrice.Text.Trim().Length > 0)
        unitPriceValue = Decimal.Parse(unitPrice.Text.Trim(),
            System.Globalization.NumberStyles.Currency);
    // Call the ProductsBLL's UpdateProduct method...
    ProductsBLL productsAPI = new ProductsBLL();
    productsAPI.UpdateProduct(productNameValue, unitPriceValue, productID);
    // Revert the DataList back to its pre-editing state
    Products.EditItemIndex = -1;
    Products.DataBind();
}

En el caso de una entrada no válida que puede estar en forma de un precio unitario con formato incorrecto, se generará una excepción un valor de precio unitario ilegal como -$5.00 o la omisión del nombre del producto. Dado que 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 entorno de ejecución de ASP.NET, donde se mostrará al usuario final (consulte 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 UpdateCommand controlador de eventos, BLL o DAL. Por ejemplo, si un usuario escribe un precio demasiado caro, la Decimal.Parse instrucción del UpdateCommand controlador de eventos producirá una FormatException excepción. Si el usuario omite el nombre del producto o si el precio tiene un valor negativo, DAL generará una excepción.

Cuando se produce una excepción, queremos mostrar un mensaje informativo dentro de la propia página. Agregue un control Web Label a la página cuyaID valor está establecido en ExceptionDetails. Configure el texto de la etiqueta para que se muestre en una fuente roja, extra grande, en negrita y cursiva asignando su propiedad CssClass a la clase CSS de Warning, que se define en el Styles.cssarchivo.

Cuando se produce un error, solo queremos que la etiqueta se muestre una vez. Es decir, en posteriores postbacks, el mensaje de advertencia Etiqueta debe desaparecer. Esto se puede lograr borrando la propiedad etiquetaText o la configuración de suVisible propiedadFalse en el Page_Loadcontrolador de eventos de (como hicimos en el Control de excepciones de nivel BLL y DAL en un tutorial de ASP.NET Página) o deshabilitando el soporte con el estado de vista de etiqueta. Vamos a usar la última opción.

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

Cuando se produzca una excepción, asignaremos los detalles de la excepción a la ExceptionDetails propiedad del Text control de etiqueta. Dado que su estado de vista está deshabilitado, en posteriores postbacks se perderán los cambios mediante programación de la propiedad Text, volviendo al texto predeterminado (una cadena vacía), ocultando así 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 Try ... Catch bloque al controlador de UpdateCommand eventos. La Try parte contiene código que puede provocar una excepción, mientras que el Catch bloque contiene código que se ejecuta ante una excepción. Consulte la sección Aspectos básicos del control de excepciones en la documentación de .NET Framework para obtener más información sobre el Try ... Catch bloque.

protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
    // Handle any exceptions raised during the editing process
    try
    {
        // Read in the ProductID from the DataKeys collection
        int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
        ... Some code omitted for brevity ...
    }
    catch (Exception ex)
    {
        // TODO: Display information about the exception in ExceptionDetails
    }
}

Cuando se produce una excepción de cualquier tipo por código dentro del Try bloque, el Catch código del bloque comenzará a ejecutarse. El tipo de excepción que se produce DbException, NoNullAllowedException, ArgumentException, etc. depende de lo que, exactamente, precipita el error en primer lugar. Si hay un problema en el nivel de base de datos, se producirá una DbException excepción. Si se especifica un valor no válido para los UnitPricecampos, UnitsInStock, UnitsOnOrdero ReorderLevel, se producirá una ArgumentException excepción, ya que se ha agregado código para validar estos valores de campo en la ProductsDataTable clase (consulte el tutorial Creación de una capa lógica de negocios).

Podemos proporcionar una explicación más útil al usuario final si basamos el texto del mensaje en el tipo de excepción detectado. El código siguiente que se usó en un formulario casi idéntico en el Control de excepciones de nivel BLL y DAL en un tutorial de ASP.NET Page proporciona este nivel de detalle:

private void DisplayExceptionDetails(Exception ex)
{
    // Display a user-friendly message
    ExceptionDetails.Text = "There was a problem updating the product. ";
    if (ex is System.Data.Common.DbException)
        ExceptionDetails.Text += "Our database is currently experiencing problems.
            Please try again later.";
    else if (ex is NoNullAllowedException)
        ExceptionDetails.Text += "There are one or more required fields that are
            missing.";
    else if (ex is ArgumentException)
    {
        string paramName = ((ArgumentException)ex).ParamName;
        ExceptionDetails.Text +=
            string.Concat("The ", paramName, " value is illegal.");
    }
    else if (ex is ApplicationException)
        ExceptionDetails.Text += ex.Message;
}

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

Con el Try ... Catch bloque en su lugar, los usuarios presentan un mensaje de error más informativo, como se muestra en las figuras 4 y 5. Tenga en cuenta que, en la cara de una excepción, 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 Catch bloque, pasando el código que devuelve DataList a su estado de edición previa.

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 imagende tamaño completo)

An Error Message is Displayed When Entering a Negative Price

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

Resumen

GridView y ObjectDataSource proporcionan controladores de eventos posteriores que incluyen información sobre las excepciones que se generaron 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. Sin embargo, estas características no están disponibles cuando se trabaja con DataList y se usa el BLL directamente. En su lugar, somos responsables de implementar el control de excepciones.

En este tutorial hemos visto cómo agregar el control de excepciones a un flujo de trabajo de actualización de DataList editable agregando un bloque de 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, mostrando información útil en la etiqueta de ExceptionDetails.

En este momento, DataList no hace ningún esfuerzo para evitar que se produzcan excepciones en primer lugar. Aunque sabemos que un precio negativo dará lugar a una excepción, aún no hemos agregado ninguna funcionalidad para evitar que un usuario entre en dicha entrada no válida. En nuestro siguiente tutorial veremos cómo ayudar a reducir las excepciones causadas por una entrada de usuario no válida agregando controles de validación en el EditItemTemplate.

¡Feliz programación!

Lecturas adicionales

Para obtener 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/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 in 24 Hours. Puede llegar a mitchell@4GuysFromRolla.com. o a través de su blog, que se puede encontrar en http://ScottOnWriting.NET.

Agradecimientos especiales a

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