miércoles, 11 de junio de 2008

DataBindings automágicos (II): una solución quiero!!!!

Bueno, seguimos con el tema, después de la presentación del problema voy a intentar ofrecer una solución aceptable y rápida.

Está claro que el método que guarda los registros no hace lo que nos gustaría que hiciese, así que vamos a cambiarlo un poco. Nos vamos al método pedidoBindingNavigatorSaveItem_Click y añadimos el siguente código:

private void pedidoBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{

this.Validate();

this.pedidoBindingSource.EndEdit();

this.pedidoTableAdapter.Update(this.dataSet1.Pedido);

// Después de hacer el Update, llamamos al método Fill para que
// recargue los registros de la tabla de Pedido


this.pedidoTableAdapter.Fill(this.dataSet1.Pedido);


// Obtenemos el identificador del último registro que haya en el Pedido del Dataset
// que se debería corresponder con el que acabamos de insertar


int id;
id = (int)this.dataSet1.Pedido.Rows[this.dataSet1.Pedido.Rows.Count - 1]["id"];


// Ahora vamos a las líneas de detalle y le cambiamos el idPedido sólo a las
// que acabamos de añadir


foreach (System.Data.DataRow row in this.dataSet1.Detalle_Pedido.Rows)
{

if (row.RowState == DataRowState.Added)
row["idPedido"] = id;

}


this.detalle_PedidoBindingSource.EndEdit();


// Forzamos el Update

this.detalle_PedidoTableAdapter.Update(this.dataSet1.Detalle_Pedido);


// y nos volvemos a traer los registros de Detalle_Pedido, con lo que tendremos
// los identificadores actualizados


this.detalle_PedidoTableAdapter.Fill(this.dataSet1.Detalle_Pedido);


// Nos movemos al último registro de la cabecera
// que debería ser el que acabamos de añadir


this.pedidoBindingSource.MoveLast();

this.Refresh();

}


¿Qué problemas plantea esta solución?

Para empezar, es una solución para salir del paso (una ñapa como una casa :P), y está claro que no se puede tomar como definitiva. En entornos en los que la concurrencia sea un problema hay que usar técnicas de concurrencia y bloqueos sin remisión con lo que en vez de un problema pasamos a tener 2, la concurrencia y los interbloqueos (deadlocks y similares).

Segundo, el principal problema es que estamos suponiendo que el último registro es efectivamente el nuestro, pero eso puede ser cierto o no. Tendríamos que tener la certeza absoluta de que es así, lo que implicaría hacer consultas sobre la base de datos o bloquear registros.

Y para acabar, el último que guarda en este escenario de concurrencia tiene constancia de todos los registros en cuanto guarda, pero el primero que guarde no obtiene todos los registros hasta que introduzca uno nuevo. Esto puede ser problemático o no, dependerá de nuestro escenario particular.


Conclusiones

- Las cosas fáciles no suelen tener en cuenta los problemas complejos, que suelen ser con los que nos tenemos que pelear en la vida real. Hacer databinding con drag-and-drop están muy bien para hacer pequeños proyectos, pero cuando entra en juego la concurrencia tenemos que saber qué es lo que hay por debajo, y eso sólo se consigue si tenemos muy claro cómo trabaja ADO.NET y sus clases.

- No te preocupes, que lo que te está pasando seguro que le pasó a un millón de programadores antes y casi todos encontraron una solución. Sólo te queda encontrar la tuya, que dependerá de tu problema concreto mucho más de lo que te imaginas.

- Vamos, que no hay una solución definitiva para el tema del acceso a datos y mucho menos el de la concurrencia. Yo personalmente recomiendo usar un ORM para que mapee el modelo de base de datos en clases, que los hay muchos y muy buenos por ahí.

Eso sí, a la espera que el Entity Framework sea lo que todos esperamos que sea ;)