Estoy ahora con la serie C del curso de ADO.NET: hay novedades suficientes en la 2.0 como para redactar directamente esta parte para la nueva versión. Voy a mantener el material original, centrado en los adaptadores de datos, porque sigo creyendo que es esencial dominar el funcionamiento de
SqlDataAdapter para trabajar bien con los nuevos
table adapters. Pero es en esta serie donde voy a incluir el nuevo sistema de adaptadores de tablas, por lo que tendré que añadir unos cuantos ejercicios más.
Hay otra novedad indispensable en ADO.NET: el nuevo espacio de nombres System.Transactions. Es probable que el soporte para transacciones en ADO.NET 1.0 haya sido tan malo (sí, era muy malo) porque ya estaba entre los planes la adición de esta funcionalidad. La idea es muy sencilla: ¿quiere iniciar una transacción? Cree un objeto de la clase TransactionScope con la ayuda de una instrucción using. Todo el código que se ejecute mientras el objeto creado está vivo, estará "protegido" por la transacción. No hace falta que ese código se ejecute dentro del mismo método. La transacción se convierte en una especie de atributo global del proceso, durante todo el tiempo en que está activa. Cuando quiera confirmar la transacción, llame al método Commit de la instancia creada. Si por el contrario, abandona la sección protegida sin confirmar, los efectos de las operaciones realizadas se cancelan de forma automática.
¿A qué se parece este sistema? Efectivamente, ¡se parece a las transacciones "declarativas" de COM+! Naturalmente, la implementación de esta funcionalidad en .NET 2.0 es mucho más fácil que la implementación en COM+. En COM+ se utiliza una técnica compleja que intercepta llamadas en tiempo de ejecución. System.Transactions, por el contrario, al estar contenido dentro de la plataforma, es mucho más eficiente, sobre todo cuando no hay que lidiar con transacciones distribuidas entre distintos administradores de recursos.
Puede que, si no conoce COM+, no comprenda el motivo de mi entusiasmo. Intentaré explicarlo en dos párrafos: En cierto sentido, el manejo de transacciones se parece al manejo de excepciones. ¿En qué sentido? ¡En que no se puede encapsular! Ambas son tareas que se deben distribuir a lo largo y ancho del código fuente. Esta es una consecuencia indeseada asociada al concepto de composición modular. Suponga que escribimos un método para hallar la raíz cuadrada. Exigimos que nos proporcionen un valor positivo. ¿Qué hacer si alguien, deliberadamente o por error, nos pasa un número negativo? Cualquier cosa que hagamos es mala... ¡porque, al tratarse de un método sumamente reutilizable, no sabemos dónde ni cómo se va a usar! Lo único sensato: disparar una excepción, y dejar la responsabilidad de su manejo a quien ensamble el método en un sistema más complejo. El, o ella, sabrán mejor que nosotros cuál será la mejor política.
¿Y qué ocurre con las transacciones? Usted escribe una rutina para cargar un importe a una tarjeta de crédito. ¿Debe encerrarla en una transacción? Puede que la rutina termine usándose por separado; puede que la rutina forme parte, finalmente, de una operación mucho más compleja. Y, lo más complicado y a la vez real, puede que ocurran las dos cosas, y que la rutina sea utilizada en los contextos más diversos. Tiene sentido hablar del éxito local del cargo en tarjeta. Si esta operación falla, y en el contexto que se ejecuta no existe una alternativa (como el pago por transferencia), la operación debe fallar. Pero si triunfa, esto no significa que el proceso que la engloba haya también triunfado. En todos caso, se tratará de un voto a favor de una de las partes del sistema.
Por este motivo son importantes sistemas como COM+ y el nuevo sistema en .NET: permiten "componer" operaciones que trabajan con transacciones. Y esto significa que es mucho más sencillo crear módulos verdaderamente reutilizables.