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.
Aprenda a insertar varios registros de base de datos en una sola operación. En la capa de interfaz de usuario ampliamos GridView para permitir que el usuario escriba varios registros nuevos. En la capa de acceso a datos encapsulamos las múltiples operaciones de inserción dentro de una transacción para asegurarnos de que todas las inserciones tengan éxito o que todas las inserciones sean revertidas.
Introducción
En el tutorial actualización por lotes hemos visto cómo personalizar el control GridView para presentar una interfaz en la que se pueden editar varios registros. El usuario que visita la página podría realizar una serie de cambios y, a continuación, hacer clic en un solo botón, realizar una actualización por lotes. En situaciones en las que los usuarios suelen actualizar muchos registros de una sola vez, una interfaz de este tipo puede ahorrar incontables clics y cambios de contexto entre teclado y ratón en comparación con las funciones de edición predeterminadas por fila que se exploraron por primera vez en el tutorial An Overview of Inserting, Updating, and Deleting Data (Una visión general de la inserción, actualización y eliminación de datos).
Este concepto también se puede aplicar al agregar registros. Imagine que aquí en Northwind Traders normalmente recibimos envíos de proveedores que contienen una serie de productos para una categoría determinada. Por ejemplo, podríamos recibir un envío de seis productos de té y café diferentes de Tokyo Traders. Si un usuario escribe los seis productos uno a la vez a través de un control DetailsView, tendrá que elegir muchos de los mismos valores una y otra vez: tendrá que elegir la misma categoría (Bebidas), el mismo proveedor (Tokyo Traders), el mismo valor discontinuo (False) y las mismas unidades en el valor de pedido (0). Esta entrada de datos repetitiva no solo consume mucho tiempo, sino que es propensa a errores.
Con un poco de trabajo podemos crear una interfaz de inserción por lotes que permita al usuario elegir el proveedor y la categoría una vez, escribir una serie de nombres de producto y precios unitarios y, a continuación, hacer clic en un botón para agregar los nuevos productos a la base de datos (vea la figura 1). A medida que se agrega cada producto, a sus campos de datos ProductName
y UnitPrice
se les asignan los valores especificados en los cuadros de texto, mientras que a sus valores CategoryID
y SupplierID
se les asignan los valores de las listas desplegables en la parte de arriba del formulario. Los valores Discontinued
y UnitsOnOrder
se establecen en los valores fijos de false
y 0, respectivamente.
Figura 1: Interfaz de inserción por lotes (haga clic para ver la imagen de tamaño completo)
En este tutorial, crearemos una página que implemente la interfaz de inserción por lotes que se muestra en la figura 1. Al igual que con los dos tutoriales anteriores, envolveremos las inserciones dentro del ámbito de una transacción para garantizar la atomicidad. ¡Comencemos!
Paso 1: Crear la interfaz de visualización
Este tutorial constará de una sola página que se divide en dos regiones: una región para mostrar y una región de inserción. La interfaz de visualización, que crearemos en este paso, muestra los productos en gridView e incluye un botón titulado Procesar envío de productos. Cuando se hace clic en este botón, la interfaz de presentación se reemplaza por la interfaz de inserción, que se muestra en la figura 1. La interfaz de presentación vuelve después de hacer clic en los botones Agregar productos del envío o Cancelar. Crearemos la interfaz de inserción en el paso 2.
Al crear una página que tenga dos interfaces, solo una de las cuales está visible a la vez, cada interfaz normalmente se coloca dentro de un control web de panel, que actúa como contenedor para otros controles. Por lo tanto, nuestra página tendrá dos controles panel uno para cada interfaz.
Para empezar, abra la BatchInsert.aspx
página en la BatchData
carpeta y arrastre un Panel desde el Cuadro de herramientas al Diseñador (vea la figura 2). Establezca la propiedad del panel ID
en DisplayInterface
. Al agregar el Panel al Diseñador, sus Height
propiedades y Width
se establecen en 50px y 125px, respectivamente. Borre estos valores de propiedad en la ventana Propiedades.
Figura 2: Arrastrar un panel desde el cuadro de herramientas al Diseñador (haga clic para ver la imagen de tamaño completo)
A continuación, arrastre un control Button y GridView al Panel. Establezca la propiedad del botón ID
en ProcessShipment
y la propiedad Text
del botón en Procesar el envío del producto. Establezca la propiedad ID
de GridView en ProductsGrid
y, desde su etiqueta inteligente, vincule a un nuevo ObjectDataSource denominado ProductsDataSource
. Configure ObjectDataSource para extraer sus datos del método ProductsBLL
de la clase GetProducts
. Puesto que gridView solo se usa para mostrar datos, establezca las listas desplegables en las pestañas UPDATE, INSERT y DELETE en (Ninguno). Haga clic en Finalizar para completar el asistente para configurar los orígenes de datos.
Figura 3: Mostrar los datos devueltos desde el método de la ProductsBLL
clase (GetProducts
haga clic para ver la imagen de tamaño completo)
Figura 4: Establecer las listas de Drop-Down en las pestañas UPDATE, INSERT y DELETE en (Ninguno) (Haga clic para ver la imagen de tamaño completo)
Después de completar el asistente ObjectDataSource, Visual Studio agregará BoundFields y checkBoxField para los campos de datos del producto. Quite todos los campos excepto ProductName
, CategoryName
, SupplierName
, UnitPrice
y Discontinued
. No dude en realizar cualquier personalización estética. Decidí dar formato al UnitPrice
campo como un valor de moneda, reordenar los campos y cambiar el nombre de varios de los valores de los campos HeaderText
. Configure también GridView para incluir compatibilidad de paginación y ordenación activando las casillas Habilitar paginación y Habilitar ordenación en la etiqueta inteligente de GridView.
Después de agregar los controles Panel, Button, GridView y ObjectDataSource y personalizar los campos de GridView, el marcado declarativo de la página debe ser similar al siguiente:
<asp:Panel ID="DisplayInterface" runat="server">
<p>
<asp:Button ID="ProcessShipment" runat="server"
Text="Process Product Shipment" />
</p>
<asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True"
AllowSorting="True" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice">
<ItemStyle HorizontalAlign="Right" />
</asp:BoundField>
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemStyle HorizontalAlign="Center" />
</asp:CheckBoxField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
</asp:Panel>
Tenga en cuenta que el marcado de Button y GridView aparece dentro de las etiquetas de abertura y cierre <asp:Panel>
. Puesto que estos controles están dentro del DisplayInterface
Panel, podemos ocultarlos simplemente estableciendo la propiedad Visible
del Panel en false
. El paso 3 examina el cambio programado de la propiedad del Visible
Panel en respuesta a un clic en un botón para mostrar una interfaz mientras oculta la otra.
Dedique un momento a ver nuestro progreso a través de un explorador. Como se muestra en la figura 5, debería ver un botón Procesar envío de productos encima de gridView que muestra los productos diez a la vez.
Figura 5: GridView enumera las funcionalidades de ordenación y paginación de productos y ofertas (haga clic para ver la imagen de tamaño completo)
Paso 2: Crear la interfaz de inserción
Con la interfaz de visualización completa, estamos listos para crear la interfaz de inserción. En este tutorial, vamos a crear una interfaz de inserción que solicite un único valor de proveedor y categoría y, a continuación, permita al usuario escribir hasta cinco nombres de producto y valores de precio unitario. Con esta interfaz, el usuario puede agregar uno a cinco nuevos productos que comparten la misma categoría y proveedor, pero tienen nombres y precios únicos de productos.
Empiece arrastrando un Panel desde el Cuadro de herramientas al Diseñador y colocándolo debajo del Panel existente DisplayInterface
. Establezca la ID
propiedad de este Panel recién agregado en InsertingInterface
y establezca su Visible
propiedad en false
. Agregaremos código que establece la propiedad InsertingInterface
del Visible
Panel a true
en el paso 3. Borre también los valores de propiedad y Height
del Width
panel.
A continuación, es necesario crear la interfaz de inserción que se mostró de nuevo en la figura 1. Esta interfaz se puede crear a través de una variedad de técnicas HTML, pero usaremos una bastante sencilla: una tabla de cuatro columnas y siete filas.
Nota:
Al escribir el marcado para los elementos HTML <table>
, prefiero usar la vista Origen. Aunque Visual Studio tiene herramientas para agregar <table>
elementos a través del Diseñador, el Diseñador parece demasiado dispuesto a inyectar configuraciones no solicitadas de style
en el marcado. Una vez que he creado el <table>
marcado, normalmente vuelvo al entorno de diseño para agregar los controles Web y configurar sus propiedades. Al crear tablas con columnas y filas determinadas previamente, prefiero usar HTML estático en lugar del control Web de tabla porque solo se puede tener acceso a los controles Web de tabla colocados en un control Web de tabla mediante el FindControl("controlID")
patrón . Sin embargo, uso controles web de tabla para tablas de tamaño dinámico (cuyas filas o columnas se basan en algunos criterios especificados por el usuario o base de datos), ya que el control Web de tabla se puede construir mediante programación.
Escriba el siguiente código de marcado dentro de las etiquetas <asp:Panel>
del Panel InsertingInterface
.
<table class="DataWebControlStyle" cellspacing="0">
<tr class="BatchInsertHeaderRow">
<td class="BatchInsertLabel">Supplier:</td>
<td></td>
<td class="BatchInsertLabel">Category:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertFooterRow">
<td colspan="4">
</td>
</tr>
</table>
Este <table>
marcado aún no incluye ningún control web, los agregaremos en breve. Tenga en cuenta que cada <tr>
elemento contiene una configuración de clase CSS determinada: BatchInsertHeaderRow
para la fila de encabezado donde irán las listas desplegables de proveedor y categoría; BatchInsertFooterRow
para la fila de pie de página donde irán los botones Agregar productos de envío y Cancelar; y alternando los valores BatchInsertRow
y BatchInsertAlternatingRow
para las filas que contendrán los controles de cuadro de texto de producto y precio unitario. He creado las clases CSS correspondientes en el Styles.css
archivo para dar a la interfaz de inserción una apariencia similar a los controles GridView y DetailsView que hemos usado en estos tutoriales. Estas clases CSS se muestran a continuación.
/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
font-weight: bold;
text-align: right;
}
.BatchInsertHeaderRow td
{
color: White;
background-color: #900;
padding: 11px;
}
.BatchInsertFooterRow td
{
text-align: center;
padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
background-color: #fcc;
}
Con este marcado introducido, regrese a la vista Diseño. Esto <table>
debe mostrarse como una tabla de cuatro columnas y siete filas en el Diseñador, como se muestra en la figura 6.
Figura 6: La interfaz de inserción se compone de una tabla de cuatro columnas Seven-Row (haga clic para ver la imagen de tamaño completo)
Ahora estamos listos para agregar los controles web a la interfaz de inserción. Arrastra dos listas desplegables desde el cuadro de herramientas a las celdas adecuadas de la tabla, una para el proveedor y otra para la categoría.
Establezca la propiedad DropDownList del ID
proveedor en Suppliers
y vincule a un objeto ObjectDataSource denominado SuppliersDataSource
. Configure el nuevo ObjectDataSource para recuperar sus datos del método SuppliersBLL
de la clase GetSuppliers
y establecer la lista desplegable de la pestaña UPDATE en (Ninguno). Haga clic en Finalizar para completar el asistente.
Figura 7: Configurar objectDataSource para usar el método de clase s SuppliersBLL
(GetSuppliers
imagen de tamaño completo)
Suppliers
Haga que DropDownList muestre el CompanyName
campo de datos y use el SupplierID
campo de datos como sus ListItem
valores.
Figura 8: Mostrar el CompanyName
campo de datos y usar SupplierID
como valor (haga clic para ver la imagen de tamaño completo)
Asigne el nombre DropDownList al segundo elemento DropDownList Categories
y asígnelo a un nuevo objectDataSource denominado CategoriesDataSource
. Configure el ObjectDataSource CategoriesDataSource
para usar el método de la clase CategoriesBLL
GetCategories
; establezca las listas desplegables en las pestañas "UPDATE" y "DELETE" en (Ninguno) y haga clic en Finalizar para completar el asistente. Por último, haga que DropDownList muestre el CategoryName
campo de datos y use CategoryID
como valor .
Una vez agregados estos dos DropDownLists y enlazados a ObjectDataSources configurados adecuadamente, la pantalla debe tener un aspecto similar a la figura 9.
Figura 9: La fila de encabezado contiene ahora las Suppliers
listas desplegables y (Categories
haga clic para ver la imagen de tamaño completo)
Ahora es necesario crear los Cuadros de texto para recopilar el nombre y el precio de cada nuevo producto. Arrastre un control TextBox desde el Cuadro de herramientas al Diseñador para cada una de las cinco filas de nombre de producto y precio. Establezca las propiedades de los cuadros de texto en ID
, ProductName1
, UnitPrice1
, ProductName2
, UnitPrice2
, ProductName3
, etc.
Agregue un CompareValidator después de cada uno de los cuadros de texto de precio unitario, estableciendo la propiedad ControlToValidate
a la correspondiente ID
. Establezca también la propiedad Operator
en GreaterThanEqual
, ValueToCompare
en 0 y Type
en Currency
. Esta configuración indica al CompareValidator que asegúrese de que el precio, si se especifica, es un valor de moneda válido mayor o igual que cero. Establezca la Text
propiedad en *y ErrorMessage
en El precio debe ser mayor o igual que cero. Además, omita los símbolos de moneda.
Nota:
La interfaz de inserción no incluye ningún control RequiredFieldValidator, aunque el ProductName
campo de la Products
tabla de base de datos no permita NULL
valores. Esto se debe a que queremos permitir que el usuario escriba hasta cinco productos. Por ejemplo, si el usuario proporcionara el nombre del producto y el precio unitario de las tres primeras filas, dejando en blanco las dos últimas filas, simplemente agregaríamos tres nuevos productos al sistema. Puesto ProductName
que es necesario, sin embargo, tendremos que comprobar mediante programación que si se especifica un precio unitario que se proporciona un valor de nombre de producto correspondiente. Abordaremos esta comprobación en el paso 4.
Al validar la entrada del usuario, CompareValidator notifica datos no válidos si el valor contiene un símbolo de moneda. Agregue un $ delante de cada uno de los cuadros de texto de precio unitario para servir como una indicación visual que indique al usuario que omita el símbolo de moneda al escribir el precio.
Por último, agregue un control ValidationSummary dentro del InsertingInterface
Panel, configure su ShowMessageBox
propiedad en true
y su ShowSummary
propiedad en false
. Con esta configuración, si el usuario escribe un valor de precio unitario no válido, aparecerá un asterisco junto a los controles TextBox infractores y ValidationSummary mostrará un cuadro de mensajes del lado cliente que muestra el mensaje de error que especificó anteriormente.
En este punto, la pantalla debe ser similar a la figura 10.
Figura 10: La interfaz de inserción ahora incluye cuadros de texto para los nombres y precios de productos (haga clic para ver la imagen de tamaño completo)
A continuación, necesitamos agregar los botones Agregar productos desde envío y Cancelar a la fila de pie de página. Arrastre dos controles De botón desde el Cuadro de herramientas al pie de página de la interfaz de inserción, estableciendo las propiedades Botones ID
en AddProducts
y CancelButton
las Text
propiedades en Agregar productos desde envío y Cancelar, respectivamente. Además, establezca la propiedad CancelButton
s del CausesValidation
false
control en .
Por último, es necesario agregar un control de etiqueta web que mostrará mensajes de estado para las dos interfaces. Por ejemplo, cuando un usuario agrega correctamente un nuevo envío de productos, queremos volver a la interfaz de presentación y mostrar un mensaje de confirmación. Sin embargo, si el usuario proporciona un precio para un nuevo producto, pero deja el nombre del producto, es necesario mostrar un mensaje de advertencia, ya que el ProductName
campo es obligatorio. Puesto que necesitamos que se muestre este mensaje para ambas interfaces, colóquelo en la parte superior de la página fuera de los paneles.
Arrastre un control Etiqueta Web desde el Cuadro de herramientas hasta la parte superior de la página en el Diseñador. Establezca la propiedad ID
en StatusLabel
, desactive la propiedad Text
y establezca las propiedades Visible
y EnableViewState
en false
. Como hemos visto en los tutoriales anteriores, establecer la EnableViewState
propiedad en false
nos permite cambiar mediante programación los valores de propiedad label s y hacer que vuelvan automáticamente a sus valores predeterminados en el postback posterior. Esto simplifica el código para mostrar un mensaje de estado en respuesta a alguna acción del usuario que desaparece en el postback posterior. Por último, establezca la propiedad s StatusLabel
del control CssClass
a Warning, que es el nombre de una clase CSS definida en Styles.css
que muestra el texto en una fuente grande, cursiva, negrita y roja.
En la figura 11 se muestra el Diseñador de Visual Studio después de agregar y configurar la etiqueta.
Figura 11: Colocar el StatusLabel
control encima de los dos controles de panel (haga clic para ver la imagen de tamaño completo)
Paso 3: Cambiar entre las interfaces de visualización e inserción
En este momento hemos completado el marcado de nuestras interfaces de visualización e inserción, pero todavía nos quedamos con dos tareas:
- Intercambio entre las interfaces de visualización e inserción
- Adición de los productos en el envío a la base de datos
Actualmente, la interfaz de visualización está visible, pero la interfaz de inserción está oculta. Esto se debe a que la DisplayInterface
propiedad del Visible
Panel está establecida en true
(el valor predeterminado), mientras que la InsertingInterface
propiedad del Visible
Panel está establecida false
en . Para cambiar entre las dos interfaces, basta con alternar el valor de propiedad de cada control Visible
.
Queremos pasar de la interfaz de presentación a la interfaz de inserción cuando se hace clic en el botón Procesar envío de producto. Por lo tanto, cree un controlador de eventos para este evento Button s Click
que contenga el código siguiente:
protected void ProcessShipment_Click(object sender, EventArgs e)
{
DisplayInterface.Visible = false;
InsertingInterface.Visible = true;
}
Este código simplemente oculta el DisplayInterface
Panel y muestra el InsertingInterface
Panel.
A continuación, cree controladores de eventos para los controles Add Products from Shipment and Cancel Button (Agregar productos desde envío y cancelar botón) en la interfaz de inserción. Cuando se hace clic en cualquiera de estos botones, es necesario volver a la interfaz de visualización. Cree Click
controladores de eventos para los dos controles Button de manera que llamen a ReturnToDisplayInterface
, un método que agregaremos en breve. Además de ocultar el InsertingInterface
Panel y mostrar el DisplayInterface
Panel, el ReturnToDisplayInterface
método debe devolver los controles Web a su estado de edición previa. Esto implica establecer las propiedades DropDownLists SelectedIndex
en 0 y borrar las Text
propiedades de los controles TextBox.
Nota:
Tenga en cuenta lo que puede ocurrir si no devolvimos los controles a su estado de edición previa antes de volver a la interfaz de presentación. Un usuario podría hacer clic en el botón Procesar envío de producto, escribir los productos del envío y, a continuación, hacer clic en Agregar productos desde el envío. Esto agregaría los productos y devolvería al usuario a la interfaz de visualización. En este momento, es posible que el usuario quiera agregar otro envío. Al hacer clic en el botón Procesar envío de producto, volverían a la interfaz de inserción, pero las selecciones DropDownList y los valores textBox se rellenarían con sus valores anteriores.
protected void AddProducts_Click(object sender, EventArgs e)
{
// TODO: Save the products
// Revert to the display interface
ReturnToDisplayInterface();
}
protected void CancelButton_Click(object sender, EventArgs e)
{
// Revert to the display interface
ReturnToDisplayInterface();
}
const int firstControlID = 1;
const int lastControlID = 5;
private void ReturnToDisplayInterface()
{
// Reset the control values in the inserting interface
Suppliers.SelectedIndex = 0;
Categories.SelectedIndex = 0;
for (int i = firstControlID; i <= lastControlID; i++)
{
((TextBox)InsertingInterface.FindControl("ProductName" + i.ToString())).Text =
string.Empty;
((TextBox)InsertingInterface.FindControl("UnitPrice" + i.ToString())).Text =
string.Empty;
}
DisplayInterface.Visible = true;
InsertingInterface.Visible = false;
}
Ambos Click
controladores de eventos simplemente llaman al ReturnToDisplayInterface
método , aunque volveremos al controlador de eventos Add Products from Shipment Click
en el paso 4 y agregaremos código para guardar los productos.
ReturnToDisplayInterface
comienza devolviendo las listas desplegables de Suppliers
y Categories
a sus primeras opciones. Las dos constantes firstControlID
y lastControlID
marcan los valores de índice de control inicial y final utilizados en la denominación de los cuadros de texto del nombre del producto y del precio unitario en la interfaz de inserción, y se utilizan en los límites del bucle for
que restablece las propiedades Text
de los controles TextBox a una cadena vacía. Por último, las propiedades Paneles Visible
se restablecen para que la interfaz de inserción esté oculta y se muestre la interfaz de visualización.
Dedique un momento a probar esta página en un explorador. Al visitar por primera vez la página debería ver la interfaz de presentación tal como se muestra en la figura 5. Haga clic en el botón Procesar envío de producto. La página se actualizará, y ahora debería ver la interfaz de inserción como se muestra en la figura 12. Al hacer clic en los botones Agregar productos desde envío o Cancelar, se le devuelve a la interfaz de presentación.
Nota:
Al ver la interfaz de inserción, tómese un momento para probar los CompareValidators en los cuadros de texto del precio unitario. Debería ver una advertencia del cuadro de diálogo del lado del cliente al hacer clic en el botón Agregar productos del envío con valores de moneda no válidos o precios con un valor inferior a cero.
Figura 12: La interfaz de inserción se muestra después de hacer clic en el botón Procesar envío de producto (haga clic para ver la imagen de tamaño completo)
Paso 4: Agregar los productos
Todo lo que queda por hacer en este tutorial es guardar los productos en la base de datos utilizando el controlador de eventos del botón Click
Agregar productos desde el envío. Esto se puede lograr creando ProductsDataTable
y agregando una ProductsRow
instancia para cada uno de los nombres de producto proporcionados. Una vez agregados estos ProductsRow
elementos, realizaremos una llamada al método ProductsBLL
de la clase UpdateWithTransaction
pasando en el ProductsDataTable
. Recuerde que el método UpdateWithTransaction
, que se creó anteriormente en el tutorial Encapsulamiento de las modificaciones de la base de datos dentro de una transacción, pasa el ProductsDataTable
al método ProductsTableAdapter
UpdateWithTransaction
. Desde allí, se inicia una transacción de ADO.NET y el TableAdapter emite una instrucción INSERT
a la base de datos por cada ProductsRow
agregado en el DataTable. Suponiendo que todos los productos se agregan sin error, la transacción se confirma; de lo contrario, se revierte.
El código del controlador de eventos del botón Add Products from Shipment Click
debe también realizar un poco de comprobación de errores. Dado que no se utilizan RequiredFieldValidators en la interfaz de inserción, un usuario podría introducir un precio para un producto sin introducir su nombre. Dado que se requiere el nombre del producto, si se desarrolla esta condición, es necesario alertar al usuario y no continuar con las inserciones. El código completo Click
del controlador de eventos sigue:
protected void AddProducts_Click(object sender, EventArgs e)
{
// Make sure that the UnitPrice CompareValidators report valid data...
if (!Page.IsValid)
return;
// Add new ProductsRows to a ProductsDataTable...
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
for (int i = firstControlID; i <= lastControlID; i++)
{
// Read in the values for the product name and unit price
string productName = ((TextBox)InsertingInterface.FindControl
("ProductName" + i.ToString())).Text.Trim();
string unitPrice = ((TextBox)InsertingInterface.FindControl
("UnitPrice" + i.ToString())).Text.Trim();
// Ensure that if unitPrice has a value, so does productName
if (unitPrice.Length > 0 && productName.Length == 0)
{
// Display a warning and exit this event handler
StatusLabel.Text = "If you provide a unit price you must also " +
"include the name of the product.";
StatusLabel.Visible = true;
return;
}
// Only add the product if a product name value is provided
if (productName.Length > 0)
{
// Add a new ProductsRow to the ProductsDataTable
Northwind.ProductsRow newProduct = products.NewProductsRow();
// Assign the values from the web page
newProduct.ProductName = productName;
newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue);
newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue);
if (unitPrice.Length > 0)
newProduct.UnitPrice = Convert.ToDecimal(unitPrice);
// Add any "default" values
newProduct.Discontinued = false;
newProduct.UnitsOnOrder = 0;
products.AddProductsRow(newProduct);
}
}
// If we reach here, see if there were any products added
if (products.Count > 0)
{
// Add the new products to the database using a transaction
ProductsBLL productsAPI = new ProductsBLL();
productsAPI.UpdateWithTransaction(products);
// Rebind the data to the grid so that the products just added are displayed
ProductsGrid.DataBind();
// Display a confirmation (don't use the Warning CSS class, though)
StatusLabel.CssClass = string.Empty;
StatusLabel.Text = string.Format(
"{0} products from supplier {1} have been added and filed under " +
"category {2}.", products.Count, Suppliers.SelectedItem.Text,
Categories.SelectedItem.Text);
StatusLabel.Visible = true;
// Revert to the display interface
ReturnToDisplayInterface();
}
else
{
// No products supplied!
StatusLabel.Text = "No products were added. Please enter the product " +
"names and unit prices in the textboxes.";
StatusLabel.Visible = true;
}
}
El controlador de eventos se inicia asegurándose de que la Page.IsValid
propiedad devuelve un valor de true
. Si devuelve false
, significa que uno o varios de los CompareValidators notifican datos no válidos; en tal caso no queremos intentar insertar los productos especificados o terminaremos con una excepción al intentar asignar el valor de precio unitario especificado por el usuario a la ProductsRow
propiedad s UnitPrice
.
A continuación, se crea una nueva ProductsDataTable
instancia (products
). Se usa un for
bucle para recorrer el nombre del producto y los cuadros de texto del precio unitario, y las Text
propiedades se leen en las variables locales productName
y unitPrice
. Si el usuario ha escrito un valor para el precio unitario, pero no para el nombre de producto correspondiente, StatusLabel
muestra el mensaje Si proporciona un precio unitario, también debe incluir el nombre del producto y el controlador de eventos se cierra.
Si se ha proporcionado un nombre de producto, se crea una nueva ProductsRow
instancia mediante el ProductsDataTable
método s NewProductsRow
. Esta nueva propiedad de la instancia ProductsRow
se establece al TextBox del nombre de producto actual, mientras que las propiedades ProductName
y SupplierID
se asignan a las propiedades CategoryID
de los DropDownLists en el encabezado de la interfaz de inserción. Si el usuario introdujo un valor para el precio del producto, este se asigna a la propiedad ProductsRow
de la instancia UnitPrice
; de lo contrario, la propiedad se deja sin asignar, lo que resultará en un valor NULL
para UnitPrice
en la base de datos. Por último, las Discontinued
propiedades y UnitsOnOrder
se asignan a los valores false
codificados de forma rígida y 0, respectivamente.
Una vez asignadas las propiedades a la instancia ProductsRow
, se agrega a la ProductsDataTable
.
Al finalizar el for
bucle, comprobamos si se han agregado productos. Después de todo, el usuario podrá hacer clic en agregar productos del envío antes de introducir los nombres o precios de los productos. Si hay al menos un producto en ProductsDataTable
, se llama al método de la ProductsBLL
clase s UpdateWithTransaction
. A continuación, los datos se vuelven a enlazar a ProductsGrid
GridView para que los productos recién agregados aparezcan en la interfaz de presentación.
StatusLabel
se actualiza para mostrar un mensaje de confirmación y ReturnToDisplayInterface
se invoca, ocultando la interfaz de inserción y mostrando la interfaz de presentación.
Si no se ha ingresado ningún producto, la interfaz de inserción permanece visible, pero el mensaje No se ha agregado ningún producto. Escriba los nombres de producto y los precios unitarios en los cuadros de texto.
En la figura 13, 14 y 15 se muestran las interfaces de inserción y visualización en acción. En la figura 13, el usuario ha escrito un valor de precio unitario sin un nombre de producto correspondiente. En la figura 14 se muestra la interfaz de presentación después de que se hayan agregado correctamente tres nuevos productos, mientras que la figura 15 muestra dos de los productos recién agregados en GridView (el tercero está en la página anterior).
Figura 13: Se requiere un nombre de producto al especificar un precio unitario (haga clic para ver la imagen de tamaño completo)
Figura 14: Se han agregado tres nuevas verduras para el proveedor Mayumi s (haga clic para ver la imagen de tamaño completo)
Figura 15: Los nuevos productos se pueden encontrar en la última página de GridView (haga clic para ver la imagen de tamaño completo)
Nota:
La lógica de inserción por lotes usada en este tutorial encapsula las inserciones dentro del ámbito de la transacción. Para comprobarlo, introduzca deliberadamente un error a nivel de base de datos. Por ejemplo, en lugar de asignar la propiedad ProductsRow
de la nueva instancia CategoryID
al valor seleccionado en el Categories
DropDownList, asígnela a un valor como i * 5
. Este i
es el indizador de bucle y tiene valores comprendidos entre 1 y 5. Por lo tanto, al agregar dos o más productos en la inserción por lotes, el primer producto tendrá un valor válido CategoryID
(5), pero los productos posteriores tendrán CategoryID
valores que no coincidan con CategoryID
los valores de la Categories
tabla. El efecto neto es que, aunque la primera INSERT
se realizará correctamente, las posteriores producirán un error por violación de restricción de clave externa. Dado que la inserción por lotes es atómica, la primera INSERT
se revertirá, devolviendo la base de datos a su estado antes de que se inicie el proceso de inserción por lotes.
Resumen
En este y en los dos tutoriales anteriores hemos creado interfaces que permiten actualizar, eliminar e insertar lotes de datos, todos los cuales usaron la compatibilidad con la transacción que agregamos a la capa de acceso a datos en el tutorial Sobrecapsulación de modificaciones de base de datos dentro de un tutorial de transacción . En algunos escenarios, estas interfaces de usuario de procesamiento por lotes mejoran considerablemente la eficacia del usuario final al reducir el número de clics, postbacks y conmutadores de contexto de teclado a mouse, al tiempo que mantienen la integridad de los datos subyacentes.
En este tutorial se completa nuestro análisis del trabajo con datos por lotes. El siguiente conjunto de tutoriales explora una variedad de escenarios avanzados de capa de acceso a datos, incluido el uso de procedimientos almacenados en los métodos de TableAdapter, la configuración de la conexión y el nivel de comando en dal, el cifrado de cadenas de conexión, etc.
¡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 puede contactar con él en mitchell@4GuysFromRolla.com.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Hilton Giesenow y Søren Jacob Lauritsen. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.