Entre unas cosas y otras tengo ambos blogs casi abandonados: ando muy liado en el curro (inventario, programas de CNC y una aplicación para fábrica), voy a comprarme un coche y los últimos fines de semana (cuando más escribo) no los he pasado en casa. Tengo una entrada casi terminada sobre un par de cositas sobre Visual Basic 2008 y mi aplicación Colección Anime, pero antes he tenido que solucionar un problema que me ha llevado cuatro horas entender y 10 minutos corregir. Lo posteo por si a alguien le pasa y pudiera encontrarlo útil.

Visual Basic 2008 trae un nuevo componente de acceso a datos llamado TableAdapterManager. En teoría debe simplificarnos el actualizar una base de datos partiendo de un dataset tipado con varias tablas relacionadas. El TableAdapterManager tiene un método, UpdateAll, que guarda los datos de todas las tablas del dataset (filas añadidas, modificadas y eliminadas) en el orden correcto, respetando las relaciones. En teoría debería simplificarnos las operaciones de guardado de datos en estos casos. En la práctica no es tan simple y me he encontrado con una serie de problemas. La documentación es bastante escasa, así que me ha tocado descifrar el código y ver qué hace realmente el dichoso componente.

La situación inicial es ésta: una clase de acceso a datos con un método llamado GuardarDatos que prepara el café, digo, guarda los datos. El dataset tipado cuenta con tres tablas (Series, Géneros y Géneros de serie) de las cuales, en principio, sólo se modificarán (aquí) la primera y la última, ya que de Géneros me ocupé en TablasMenores.

Los problemas que se presentaron fueron los siguientes:

  1. La conexión no puede estar abierta. En mi forma actual de hacer las cosas, uso un objeto conexión (que puede ser en combinación con un bloque Using o no, según las circunstancias) en lugar del objeto conexión propio del TableAdapter. Con el TableAdapterManager intenté hacer lo mismo, esto es, le asigno el objeto conexión mediante la propiedad Connection, abro la conexión, hago lo que tenga que hacer (en este caso, sólo el UpdateAll del TableAdapterManager), cierro la conexión y a otra cosa. Esto me dio error, porque el TableAdapterManager intenta abrir la conexión sin comprobar si ya está abierta. También se encarga de cerrarla cuando termina.

  2. Necesita TableAdapters. Vale, esto es lógico: internamente llama a los distintos TableAdapter para acceder a la base de datos. El problema es que si no hay, no los instancia. En el método UpdateAll comprueba si cada TableAdapter es Nothing y si lo es, se salta el código referente a ese TableAdapter. Esto tiene su parte interesante, pues podemos discriminar qué tablas vamos a actualizar y cuáles no, pero no he encontrado que se comente esto en la documentación, y me ha llevado un rato encontrar qué era lo que fallaba. La solución es simple: instanciar los TableAdapter necesarios y pasárselos a las respectivas propiedades que tiene el TableAdapterManager, algo así: tamSeries.SeriesTableAdapter = taSeries. Internamente el TableAdapterManager pasa su objeto Connection a cada TableAdapter que vaya a usar.

  3. El tercer problema no ha sido del TableAdapterManager sino mío, al creer que un Remove y un Delete son lo mismo. Resulta que al borrar los distintos géneros de una serie usaba el método RemoveGeneroSerieRow que se había generado con el DataTable tipado. Al ejecutar el UpdateAll del TableAdapterManager me daba un error por violación de clave externa. Revisando la documentación me encuentro con que al hacer un Remove eliminamos la fila y aceptamos los cambios, por lo que esa información no pasa a la base de datos. Lo correcto es hacer un Delete, que nos marca la fila para borrado. El lío viene porque tenemos un método RemoveMiFilaRow en el DataTable, pero no un DeleteMiFilaRow, así que hay que buscar la fila (con el método FindByMiClavePrincipal, por ejemplo) y usar su método Delete.

No he puesto código de ejemplo. Si alguien lo necesita, sólo tiene que pedirlo.

Nos vemos en el Forlon.