Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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:
- 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.
- Invoque el método.
- 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 ProductName
UnitPrice
. 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, 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;
}
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.
Figura 1: Utilice la sobrecarga de 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.
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 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);
}
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 ReadOnly
true
.
Figura 3: Configure el campo asociado Read-Only (QuantityPerUnit
)
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
.
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
.
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, la omisión del valor ProductName
provoca una NoNullAllowedException, ya que la propiedad ProductName
de la clase ProductsRow
tiene su propiedad AllowDBNull
establecida en false
; si la base de datos está inactiva, el TableAdapter iniciará 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
.
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.
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 void Page_Load(object sender, EventArgs e)
{
ExceptionDetails.Visible = false;
}
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.
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 void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
}
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 elRowUpdated
controlador de eventos; sifalse
(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 entrue
la fila GridView editada permanece en modo de edición; sifalse
(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 void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
if (e.Exception != null)
{
// Display 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 experiencing problems." +
"Please try again later.";
else if (inner is NoNullAllowedException)
ExceptionDetails.Text +=
"There are one or more required fields 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
e.ExceptionHandled = true;
// Keep the row in edit mode
e.KeepInEditMode = true;
}
}
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).
Figura 9: BoundField ProductName
debe contener un valor (haga clic para ver la imagen de tamaño completo)
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:
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];
// 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 was updated, otherwise false
return rowsAffected == 1;
}
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 ApplicationException
valor 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.
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 de gran ayuda. El revisor principal de este tutorial fue Liz Shulok. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.