Supuestos:
- BDD: Northwind.
- Tabla master: Orders. PK: OrderID, identidad autoincremental.
- Tabla details: Orders. PK: OrderID, ProductID. FK: OrderID (relación con Orders.OrderID, enforced for INSERTs and UPDATEs pero sin Cascade Update ni Cascade Delete).
- Proyecto WinForms en VS2005 (.Net Framework 2.0)
- Creación de dataset (DatasetNorthwind) creado por diseño que incluye las tablas Orders, OrderDetails y tal vez otras, y sus relaciones.
- Pantalla de 2 grids editables (nuevo, editar, borrar), donde el primero tiene un BindingSource = DatasetNorthwind.Orders, y el segundo el FK_xxx del anterior (seleccionable por diseño).
Si se ha hecho correctamente, al navegar por el primer grid se irá actualizando el segundo.
A tener en cuenta:
Si modificamos directamente en los grids (editando, añadiendo y borrando, tanto pedidos enteros como líneas), el dataset asociado (DatasetNorthwind) irá guardando las modificaciones.
En el momento de persistir dichas modificaciones a la base de datos (xxxTableAdapter.Update), se deben tener en cuenta las relaciones:
- El eliminar un pedido del grid, no elimina automáticamente sus líneas, y si quedan líneas "huérfanas" en el dataset, al hacer el Update se producirá un error.
- Al añadir un pedido, sus líneas toman el OrderID de la cabecera, pero este valor no es definitivo hasta que se hace el Update, pero este valor definitivo no se propaga por defecto a las líneas. (Algo equivalente ocurriría si se pudiera modificar el OrderID en las cabeceras, pero este caso no lo trataré).
Carga de pantalla (Form_Load)
this.ordersTableAdapter.Fill(this.dataSetNorthwind.Orders);
this.order_DetailsTableAdapter.Fill(this.dataSetNorthwind.Order_Details);
Implementar altas/bajas/editar (1er método)
Para solucionar el punto 1 a tener en cuenta, implementaremos el evento Orders_OrdersRowDeleting():
void Orders_OrdersRowDeleting(object sender, DataSetNorthwind.OrdersRowChangeEvent e) {
if (MessageBox.Show(
"Se eliminará el pedido " + e.Row.OrderID.ToString() + ".",
"Eliminar pedido",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Information)
== DialogResult.OK)
{
try {
DataSetNorthwind.Order_DetailsRow[] orderDetails = e.Row.GetOrder_DetailsRows();
foreach (DataSetNorthwind.Order_DetailsRow orderDetail in orderDetails)
{
orderDetail.Delete();
}
}
catch (Exception ex)
{
throw new Exception("No se han podido eliminar los detalles del pedido " + e.Row.OrderID.ToString() + ".", ex);
}
}
El manejador de este evento se debe insertar a mano en el Form_Load, tras el InitializeComponent( ):
this.dataSetNorthwind.Orders.OrdersRowDeleting += new DataSetNorthwind.OrdersRowChangeEventHandler(Orders_OrdersRowDeleting);
En el momento de salvar los cambios, se debe tener en cuenta el punto 2 (y el 1):
private void SaveChanges()
{
try
{
//Nuevos pedidos (cabeceras nuevas)? --> En los detalles hay que asignar el OrderID en el momento en que el pedido se da de alta
if (this.dataSetNorthwind.Orders.GetChanges(DataRowState.Added) != null)
{
foreach (DataSetNorthwind.OrdersRow order in this.dataSetNorthwind.Orders)
{
if (order.RowState == DataRowState.Added)
{
DataSetNorthwind.Order_DetailsRow[] orderDetails = order.GetOrder_DetailsRows();
this.ordersTableAdapter.Update(order);
foreach (DataSetNorthwind.Order_DetailsRow orderDetail in orderDetails)
{
orderDetail.OrderID = order.OrderID;
this.order_DetailsTableAdapter.Update(orderDetail);
}
}
}
}
//Resto: Hay que actualizar primero los detalles por si se han eliminado pedidos
//Para que el eliminar funcione correctamente, previamente se ha implementado el evento Orders_OrdersRowDeleting
this.order_DetailsTableAdapter.Update(this.dataSetNorthwind.Order_Details);
this.ordersTableAdapter.Update(this.dataSetNorthwind.Orders);
}
catch (Exception ex)
{
throw new Exception("Error intentando guardar los cambios", ex);
}
}
Implementar altas/bajas/editar (2º método)
En el DatasetNorthwind, editar la relación FK_xxx y forzar Delete Rule = Cascade y Update Rule = Cascade. En el salvar poner:
this.ordersTableAdapter.Update(this.dataSetNorthwind.Orders);
this.order_DetailsTableAdapter.Update(this.dataSetNorthwind.Order_Details);
Implementar ficha con DataBinding
Pasos:
- En el diseño de la pantalla, añadir el DatasetNorthwind.
- En cada TextBox indicar, en la propiedad (DataBindings)/Text el campo que toque del BindingSource.
- Implementar el FillByOrderID, desde el diseño del DatasetNorthwind mostrar el menú contextual sobre la tabla Orders, seleccionar AddQuery y crear el select... where OrderID = @OrderID.
- En el Form_Load:
this.ordersTableAdapter.FillByOrderID(this.dataSetNorthwind.Orders, orderID); - En el evento Guardar hacer:
/*Para los controles que tengan Data Source Update Mode = Never (DataBindings/Advanced)
foreach (System.Windows.Forms.Control control in this.Controls)
{
if (control.DataBindings["Text"] != null)
control.DataBindings["Text"].WriteValue();
}*/
this.ordersBindingSource.EndEdit(); //Es necesario para que el RowState pase de Unchanged a Modified.
this.ordersTableAdapter.Update(this.dataSetNorthwind.Orders);
this.DialogResult = DialogResult.OK; - Y en el Cancelar (o salir sin guardar):
this.ordersBindingSource.CancelEdit();