DataSets, esos malditos objetos

En este último mes me ha tocado, como parte del plan de mejoramiento del código y prácticas de desarrollo, mirar mucho código ya implementado con cierta dudosa calidad.

Tratando de que nadie se sienta herido, la calidad del código dependerá, entre varias otras cuestiones, de que tan bien se pueda adaptar a cambios, sus dependencias con otro código, la cantidad de líneas implicadas en una tarea específica, así como la capacidad de cualquier otro desarrollador de entender este código con cierta facilidad.

Teniendo en cuenta esto, mucho código que viene siendo arrastrado desde hace un par de años, o es creado actualmente por desarrolladores que vienen con un arrastre de conocimientos de hace un par de años, cuando se encuentra delante de Microsoft .Net Framework suele caer en el mal uso y sobre uso de los malditos DataSet. Utilizando estos engendros para mover la información de punta a punta dentro de la aplicación sin notar los grandes problemas que esto le traerá en un futuro no muy lejano.

Entendamos que dentro de los principales problemas que podemos encontrar dentro del uso, y en especial el mal uso, de los DataSet se encuentra la imposibilidad de asegurar la existencia de un campo específico de datos, la cantidad de elementos (Funcionalidad) no usada y por ende desperdiciada, la mal aplicación del mismo almacenando grandes volúmenes de datos en memoria, entre otros.

Pasemos al primero rápidamente para que se entienda a que me refiero. Por un lado, los DataSet suelen ir acompañados de una consulta del tipo “Select * From Usuarios“. Como el dichoso DataSet se amoldará al resultado de la consulta entonces el programador no se preocupa en asegurarse que los valores contenidos y retornados por una consulta sean medianamente óptimos, esperables. Una vez que hemos seleccionado vaya a saber qué desde la base de datos, el objeto es pasado por parámetro entre diferentes capas, función tras función hasta llegar a destino. Por lo tanto, la única certeza que tenemos de que, por ejemplo, los datos contenidos en ese objeto son usuarios es que el parámetro de retorno, la función o algún nombre de aquello donde estamos consumiendo los datos diga esto, por lo que, si tenemos suerte, tendremos una función de nombre ObtenerUsuarios de la cual, rogaremos, retorne un conjunto de usuarios. Y se debe a que en realidad, el DataSet contiene DataTables, que contiene DataColumns y DataRows, nada que ver con un usuario y que puedo haber sido modificado por cualquiera de las funciones por las cuales pasó hasta llegar al lugar donde se tiene que hacer uso de la información.

Por otro lado, el programador usa el DataSet como un simple contenedor de algunos datos y no le saca el provecho (Si hay alguno) real al mismo, ya que solo lo usará para contener un manojo de resultados de una consulta y transportar estos valores hasta su destino. Si vemos el código del objeto DataSet, solo su constructor configura variables, llama al Garbage Collector, inicializa trazadores entre otros chiches, simplemente para que luego no los usemos. Esto se ata con el tercer planteamiento, entonces: ¿Para qué tanto si no lo vamos a usar?

Lamentablemente esto viene de la mano de como muchas nuevas tecnologías son lanzadas y rápidamente aparecen cientos de ejemplos, por todos lados, mostrando su uso pero no advirtiendo de sus problemas. Por tal motivo muchos desarrolladores, al ser una tecnología fácil de usar la adoptan rápidamente pero, nuevamente, sin analizarla profundamente.

Entonces ¿Qué debemos usar? No me voy a morder la cola y envenenarme yo mismo tratando de dar una receta perfecta, misma que podría ser totalmente incorrecta en un futuro no muy lejano. Solo decir que una de las mejores aproximaciones hasta el momento sigue siendo la de tener objetos que representen el dominio del negocio, con entidades bien definidas, sabiendo que al consultar por usuarios obtendremos usuarios, y no algo inesperado. Si se opta por este camino, aquí les dejo dos funciones de código bastante simples que intentan pasar desde estos DataSet a entidades un poco más conocidas. Como todo en el código, puede ser mejorable.

El primero, es una extensión sobre los DataSet para transformar el mismo a una colección de una entidad dada:

public static Collection TranslateTo(this DataSet dataset)
where T : new()
{
var objectCollection = new Collection();

object[] att = typeof(T).GetCustomAttributes(typeof(TableMapper), false);

if (att.Length == 0)
throw new DecorationNotFound(“TableMapper decoration not found at class level.”);

string tableName = (att[0] as TableMapper).TableSource;

if (!dataset.Tables.Contains(tableName))
throw new DecorationNotFound(“Given table source name not found in current dataset.”);

DataTable dataTable = dataset.Tables[tableName];

foreach (DataRow row in dataTable.Rows)
{
T item = new T();
foreach (PropertyInfo property in item.GetType().GetProperties())
{
object[] propertyList = property.GetCustomAttributes(typeof(PropertyMapper), true);

if (propertyList.Length > 0)
{
string columnName = (propertyList[0] as PropertyMapper).ColumnName;

property.SetValue(item, row[columnName] != DBNull.Value
? row[columnName]
: null, null);
}
}
objectCollection.Add(item);
}

return objectCollection;
}

El segundo extiende los DataRow para un único registro:

public static T TranslateTo<T>(this DataRow dataRow)
where T: new()
{
var concreteObject = new T();

foreach (PropertyInfo property in concreteObject.GetType().GetProperties())
{
object[] propertyList = property.GetCustomAttributes(typeof(PropertyMapper), true);

if (propertyList.Length > 0)
{
string columnName = (propertyList[0] as PropertyMapper).ColumnName;

property.SetValue(concreteObject, dataRow[columnName] != DBNull.Value
? dataRow[columnName]
: null, null);
}
}

return concreteObject;
}

Todo esto se complementa con el uso de atributos decorativos para las entidades. Podemos ver algo de código en el siguiente link.


2 comentarios on “DataSets, esos malditos objetos”

  1. lontivero dice:

    Ufff. Hay tantas cosas en esta entrada que no sé bien por donde empezar.
    Lo primero es decir que, si bien estoy de acuerdo en la inmensa mayoría de lo que planteas acá, la calificación de malditos engendros es injusta. Los dataset son buenos componentes siempre y cuando se los utilice para aquello que fueron creados: Trabajo de manera desconectada. Estos pueden utilizarse para mantener un conjunto de datos sin acceder a la base de datos, manipularlos y guardarlos algún tiempo después, es por eso que cuentan con mecanismo de trackeo de cambios, mergeo, serialización ridículamente sencilla, etc.
    El problema es que, por algún extraño motivo, los libros y artículos sobre acceso a datos con .Net de 2004 y unos cuantos años más, no explicaban esto. Todos los ejemplos eran con DataSets y mucha gente aprendió así. Es decir, no se “enseñó” sino que se “vendió” algo. Entonces, tanto los Datasets como cualquier otro componente que se use para aquello que no fue diseñado, resulta odioso. También es algo reprochable el hecho de que los desarrolladores hagan su trabajo de manera irreflexiva, aunque esto es muy discutible.
    Por último, y en un intento por no caer en el mismo error que aquellos que usaron datasets de manera irreflexiva, tenemos que pensar si la solución que propones no tiene peca de exactamente lo mismo. Veamos, nosotros que venimos de los tiempos en que todavía no existía la OOP, luego de descubrirla y entenderla, y luego de muchos trabajos realizados y muchos ejemplos estudiados creemos que si o si necesitamos un modelo de objetos para nuestros datos (porque así han sido los ejemplos y porque siempre lo hicimos así). Claro, necesitamos acercarnos al dominio del problema y modelarlo y para ello, si el problema tiene relación con “Clientes” nosotros necesitamos tener una clase “Cliente”. Probablemente lo necesitamos y probablemente no.En las aplicaciones orientadas a datos, esta clase “Cliente” muchas veces no pasa de ser un simple DTO (algunas veces es algo más como un RowGateway o ActiveRecord). Si es un DTO, entonces es un POCO, sin responsabilidades ya que las responsabilidades se ponen en un ClienteManager o algo por el estilo. Puesto que, en la inmensa mayoría de los casos, en los sistemas orientados a datos las consultas son para mostrar sus resultados en un formulario o hacer cálculos muy sencillos, la pregunta es: ¿para qué queremos leer los resultados como objetos en lugar de leerlos como un diccionario (key/value), array o, incluso un datarow?.

    Lo curioso es que como siempre usamos un modelo de objetos, o usamos un ORM, o no usamos una base de datos relacional o bien no usamos objetos para representar nuestros datos.
    Este comentario, extremadamente largo por cierto, es para mostrar todo lo que día tras día hacemos de manera automática y sin detenernos a pensar ni un segundo en si es correcto o no. Simplemente así lo aprendimos. El ejercicio de replantearse todo el tiempo todo lo que damos por seguro es imposible.

    • Bueno, creo que sumas a lo que estaba tratando de dejar ver entre líneas. Me refiero a que, como bien dice al final del post, la propuesta puede estar totalmente errada y por lo tanto debe tomarse con cuidado, sin, en ningún momento decir que esa ES la solución. En todo caso, no puedo estar más de acuerdo con lo que plateas ya que en definitiva, creo y por lo que entiendo, adiciona más información o lo que trataba de expresar en primera instancia.

      Adicionado: En referencia al título, es solo para causar curiosidad al lector. Sabemos que el amarillismo siempre llama más que algún mensaje con tintes científicos. Creo que muy diferente hubiera sido utilizar un título como: Hagamos buen código; o: Porqué debemos leer mucho antes de tirar una línea de código. Etc.


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s