Blogia
ÉL - GURÚ

Actualizar un DataSet con PrimaryKeys autoincrementales o identidad

Por alguna razón puede que necesitemos recuperar un modelo de datos complejo, con relaciones entre tablas, a un DataSet. Durante todo el tiempo que trabajemos con este DataSet en memoria se pueden producir las tipicas operaciones CRUD (Create, Read, Update, Delete) de forma que los datos contenidos en el DataSet sean completamente diferentes a los que hay en la base de datos. Para la mayoría de casos el DataSet se maneja bastante bien a la hora de volcar los resultados de nuevo en base de datos ya que cada fila contenida en cada una de las tablas guardará su estado, mediante la enumeración DataRowState (Added, Deleted, Detached, Modified y Unchanged).

El "problema" surge cuando tenemos varios DataTable relacionados por columnas identidad o autoincrementales. En este caso mientras no vayamos a base de datos el DataSet asignará un valor propio a estas columnas que no tiene nada que ver con el que posteriormente en base de datos... ¿y donde surge el "problema"? cuando actualizamos el DataSet contra base de datos nos encontramos con que no solo tiene que hacer las operaciones necesarias CRUD contra la base de datos sino que además deberá actualizar los indices de los registros creados y a su vez de las tablas asociadas. Me he referido a problema entre comillas por que tiene una "fácil solución", también entre comillas.

Para que el DataSet se encargue de actualizar los valores de las columnas identidad y las referencias a estas en las tablas asociadas lo único que debemos hacer es asignar a la propiedad UpdateRowSource, del comando que este referenciado en la propiedad InsertCommand del DataAdapter, el valor UpdateRowSource.Both. Y ya está: las columnas se actualizarán automáticamente tanto en la tabla afectada como en las tablas relacionadas dentro del DataSet.

Pero aún nos queda el problema, sin comillas. Se trata de un problema con la forma que tienen de trabajar los DataSet en .NET cuando al insertar utilizamos el valor UpdateRowSource.Both en la propiedad UpdatedRowSource del comando que se le pasa al DataAdapter. Esta propiedad hace que al insertar un registro en una tabla con una clave autonumérica: la recoja y actualice esta clave por la que contenía el registro, dentro del DataTable en el DataSet, y, a su vez, la de todos las columnas de DataTable que tengan relaciones con esa.
El problema surge cuando el identificador devuelto de base de datos es un identificador ya existente dentro del DataTable en el DataSet. Por ejemplo: 
 

Nosotros recuperamos un conjunto de filas de las tablas A, B y C que estan relacionadas entre sí y además con la tabla D y decidimos insertar esos registros de nuevo cambiando el registro de la tabla D con el que estan relacionados. Es decir copiariamos los datos relacionados de un registro en la tabla D para otro.
Si los registros que recuperamos de la tabla A tienen los Id's 100 y 101 de la base de datos, en el DataSet se asignaran tambien estos y el Id para el siguiente registro insertado será 102.
Si insertamos otro registro en el DataSet, le asignará el Id 102, y al volcar los cambios a base de datos -no hay que olvidar que hará una inserción ya que lo único que hicimos al recuperar fue leerlos dejando los DataRow con DataRowState.Added-, se asignará para el Id 100 en el DataSet el Id 102 en base de datos. Al actualizarse automáticamente el Id del DataSet intentará ponerle 102 cosa que nos dará una excepción de clave duplicada: por el registro que habiamos insertado

Este problema es un caso muy concreto pero se nos puede reproducir en otras ocasiones como al dar de alta tantos registros más uno como los que contenga la tabla leída de base de datos, de esta forma el identificador del último elemento de la DataTable, en el DataSet, podría tratarse del identificador que la base de datos otorgué al primer elemento insertado en la tabla al volcar los datos del DataSet mediante el método Update del DataAdapter, con lo cual obtendriamos la citada excepción.

Una solución elegida es modificar la semilla de la columna del DataTable, AutoIncrementSeed, de forma que buscamos en base de datos el identificador mayor y le sumamos el número de registros recuperados de base de datos más 1. Para otros casos la solución también sería jugar con la semilla del DataTable.

2 comentarios

Jordi -

En primer lugar, felicidades por tu blog, es muy interesante.

Estoy tratando de hacer un programa que guarde los datos en una bd. Lo que pasa es que no soy informatico y a veces me quedo encallado.
Llevo vastante tiempo dandole a la cabeza para resolver el problema que expones en esta pagina. Lo que pasa es que me cuesta seguirte. :)

Se que la solucion que necesito es esta:
"Para que el DataSet se encargue de actualizar los valores de las columnas identidad y las referencias a estas en las tablas asociadas lo único que debemos hacer es asignar a la propiedad UpdateRowSource, del comando que este referenciado en la propiedad InsertCommand del DataAdapter, el valor UpdateRowSource.Both". Pero no se llevarlo a cabo.
Serias tan amable de pasarme un trozo de codigo de ejemplo?
Algo senzillo para ver por donde van los tiros.

Gracias de antemano.

Supra Vaider High -

No pains, No gains. Believe yourself. You'll be successful. Please remember never too old to learn! Let us work together.