Nivel avanzado

LINQ to SQL (I): Mapeo Objeto-Relacional


A estas alturas ya debemos de tener una idea bastante precisa del funcionamiento básico de LINQ. Hemos visto cómo lanzar consultas a listados de objetos (LINQ to Objects) e incluso algún ejemplo sobre cómo iterar sobre ficheros XML (LINQ to XML). Todas estas operaciones tienen un factor común: se realizaban sobre listados que implementaban la interfaz IEnumerable.

Además de poder lanzar consultas sobre listados de objetos, LINQ nos permite una opción aún más potente: lanzar consultas sobre bases de datos SQL Server mediante LINQ to SQL, que no es más que un mapper objeto-relacional, es decir, encargado de transformar sentencias LINQ en expresiones T-SQL y viceversa.

La potencia de LINQ to SQL radica en que, además de permitir realizar operaciones de consulta sobre una base de datos, también es posible realizar inserciones, actualizaciones y eliminaciones de forma nativa. A continuación veremos cómo realizar esto.

La principal desventaja de usar LINQ to SQL es que nuestra base de datos ha de ser, obligatoriamente, SQL Server (no obstante, existen proveedores LINQ para otras bases de datos, como LINQ to Oracle o LINQ to MySQL (vía NuGet). En caso de que todo esto falle y que necesitemos un proveedor personalizado, siempre podremos desarrollar por cuenta propia nuestro propio proveedor. En cualquier caso, siempre podremos utilizar un mapper objeto-relacional más genérico, como por ejemplo Entity Framework, del que hablaremos en posteriores artículos.

En este primer acercamiento a LINQ to SQL hablaremos de cómo establecer la relación entre nuestro programa (orientado a objetos) y nuestra base de datos (relacional). Para ello disponemos de varias posibilidades, que mostraremos a continuación:

(más…)

Sentencias en LINQ (IV): Particionado. Delegados y expresiones lambda.


El particionado es una característica que nos permite recuperar conjuntos concretos de una consulta a partir de un índice. Su funcionamiento sería similar al filtrado de no ser porque no hace uso de los datos en sí, sino del orden en el que éstos se almacenan.

Los métodos de particionado más útiles son los siguientes:

Take(n)

La sentencia Take devolverá los n primeros registros de la consulta. Así, si realizamos la siguiente consulta:

            var consulta = (from producto in DataLists.ListaProductos
                           select producto).Take(5);

Obtendremos los cinco primeros elementos de la consulta. Como podemos observar, el funcionamiento de este método es bastante sencillo.

Como punto a destacar: no se trata de una sentencia, sino de un método de la interfaz IEnumerable. Por tanto, puede aplicarse directamente a una lista sin necesidad de realizar la consulta LINQ que acabamos de hacer ahora mismo:

            var consulta = DataLists.ListaProductos.Take(5);

(más…)

Redimensión dinámica de imágenes


Siguiendo con la manipulación de imágenes en .NET, imaginemos que disponemos de una imagen de un tamaño considerable, bien en disco, bien en base de datos, y queremos enviarle al usuario un thumbnail de dicha imagen. ¡Ojo! No queremos mostrar la imagen completa con un estilo que modifique su alto y ancho, ya que con esto estaríamos enviando la imagen completa y luego modificaríamos su visualización en cliente, perdiendo el correspondiente (y valioso) ancho de banda.

En lugar de esto, sacrificaremos unos cuantos ciclos de CPU en pos de limitar el tráfico y realizaremos la operación de redimensionar la imagen en servidor para posteriormente enviarsela (más pequeñita o más grande) al cliente.
Para ello, nos crearemos una función llamada RedimensionarImagen que reciba tres parámetros: la imagen que queremos redimensionar, la nueva altura y la nueva anchura:


private System.Drawing.Bitmap RedimensionarImagen(System.Drawing.Image imagenOriginal, int alto, int ancho)
{

Lo primero que haremos será guardar el alto y el ancho de la imagen original.


int anchoOriginal = imagenOriginal.Width;
int altoOriginal = imagenOriginal.Height;

A continuación declaramos dos variables de tipo float que almacenaran el porcentaje de escalado al que se va a ver sometida la imagen.


float porcentajeEscaladoX = (float)ancho / (float)anchoOriginal;
float porcentajeEscaladoY = (float)alto / (float)altoOriginal;

El siguiente paso será definir una nueva altura para la imagen, multiplicando los valores originales por los factores que hemos obtenido en el paso anterior.


int nuevoAncho = (int)(anchoOriginal * porcentajeEscaladoX);
int nuevoAlto = (int)(altoOriginal * porcentajeEscaladoY);

Ya tenemos las dimensiones de nuestra nueva imagen, ahora necesitaremos crear una instancia.


System.Drawing.Bitmap nuevaImagen = new System.Drawing.Bitmap(nuevoAncho, nuevoAlto);

Ahora instanciamos una superficie de dibujo de GDI+ a partir de la imagen para tratar el tamaño.


System.Drawing.Graphics superficieGDI = System.Drawing.Graphics.FromImage((System.Drawing.Image)nuevaImagen);

Indicamos el modo de interpolación (en nuestro caso, Bicúbico Alta Calidad) y dibujamos la imagen con los nuevos valores de ancho y alto.


superficieGDI.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
superficieGDI.DrawImage(imagenOriginal, 0, 0, nuevoAncho, nuevoAlto);

Finalmente, liberamos el objeto GDI+ y devolvemos la imagen.


superficieGDI.Dispose();

return nuevaImagen;
}

Et voilà!, hecho esto, obtendremos una imagen con el alto y ancho deseados.

Transacciones en ADO.NET (II): Activando las Transacciones Distribuidas mediante MSDTC


Anteriormente hemos aprendido a utilizar entornos transaccionales o Transaction Scopes. El ejemplo anterior mostraba lo que se conoce como una ‘transacción ligera’ que afecta a varias tablas de una misma fuente de datos. Sin embargo, en numerosas ocasiones se nos presentará la dificultad añadida de trabajar con múltiples fuentes de datos localizadas incluso en puntos geográficos diversos.

Para solventar esta dificultad, el propio Windows nos ofrece la posibilidad de utilizar las llamadas Transacciones distribuidas. Por defecto aparecen deshabilitadas, por lo que a continuación, mostraremos cómo activarlas. Una vez activadas, utilizaremos la clase TransactionScope como si se tratara de una transacción ligera, dejando en manos del sistema operativoel control de la propia transacción.

(más…)

Abstracción de Datos (V): Aplicando el patrón Abstract Factory con C# y ADO.NET


Por norma general, un proyecto estará “atado” a una fuente de datos en particular (SQL Server, MySQL, Oracle…). Sin embargo, hay veces que esto no será así: la fuente de datos podría cambiar en cualquier momento, por lo que deberíamos ser capaces de, en la medida de lo posible, abstraer nuestra aplicación de nuestra base de datos.

Previamente comentábamos la flexibilidad otorgada por el patrón de diseño Abstract Factory. Hoy veremos una aplicación práctica, orientada precisamente a obtener práctica independencia de la fuente de datos mediante la utilización de clases comunes a todas las fuentes de datos.

Por norma general utilizamos clases específicas para lograr interactuar con una fuente de datos en concreto (por ejemplo, usaremos un objeto de la clase SqlConnection cuando trabajemos con SQL Server). Éstas clases específicas están optimizadas para trabajar con unos esquemas determinados, aportando un importante incremento del rendimiento de la aplicación. Pero como todo en esta vida, no es gratis. ¿Cuál es el precio? La portabilidad.

(más…)

Introducción a Reflection (III): Ejecutando métodos de un objeto a partir de su tipo


Ayer veíamos cómo obtener información de un objeto mediante el espacio de nombres Reflection. Ahora veremos que no solo es posible obtener información de los metadatos, sino que también es posible acceder a sus miembros.

Vimos previamente que nuestra clase  ClasePrueba tenía una función llamada sumarUno(). Vamos a intentar, mediante Reflection, invocar a la función sin necesidad de “conocer” el objeto. En realidad, y para simplificar el ejemplo, utilizaremos el nombre del método directamente, pero sería posible, a partir de una estructura MethodInfo, obtener nombre y parámetros de cada uno de los métodos y ejecutarlos de forma dinámica.

Partiendo del código del artículo anterior, añadiríamos lo siguiente para invocar el método:

 // Ejecutamos un método mediante Reflection. En primer lugar obtenemos una referencia al método
 MethodInfo Metodo = tipoObjeto.GetMethod("sumarUno");

 // A continuación, llamamos al método Invoke del tipo, pasándole como primer parámetro
 //el objeto cuyo método queremos ejecutar, y como segundo parámetro, un array de objetos con
 //los parámetros que espera el método o función.
 int dato;
 object[] parametros = new object[1] { 2 };
 dato = (int)Metodo.Invoke(objeto, parametros);

 Console.WriteLine("El resultado de la invocación ha sido: " + dato.ToString());

Recordemos que el objeto ‘objeto‘ era una instancia de ClasePrueba.

Como puntilla mostraremos otra forma de invocar un método mediante Reflection: utilizando Activator para obtener una instancia de la clase ClasePrueba y ejecutando el método Type.InvokeMember().


 // Utilizamos la clase Activator para ejecutar el método de otro objeto
 object objeto2 = Activator.CreateInstance(tipoObjeto);
 dato = (int)tipoObjeto.InvokeMember("sumarUno", BindingFlags.InvokeMethod, null, objeto2, parametros);

 Console.WriteLine("El resultado de la invocación (mediante Activator) ha sido: " + dato.ToString());

El resultado sería el siguiente:

090502Resul

Introducción a Reflection (II): Obteniendo metadatos de una clase


En artículos anteriores hemos visto la potencia de las clases contenidas en el espacio de nombres Reflection. Hoy vamos a intentar profundizar un poco más en la potencia que podemos obtener utilizando estos recursos.
Como hemos visto, Reflection proporciona clases que permiten leer los metadatos de una clase. Bien… habrá quien se pregunte… ¿qué es un metadato? Un metadato no es más que un dato que describe a otro dato. Por ejemplo, en el ámbito que vamos a ver a continuación, el nombre de la clase es en sí un metadato. De hecho, la propia clase en sí es un metadato, ya que describe un “patrón” o estructura de los objetos que derivan de ella.
Reflection nos proporciona una serie de clases que se encargan de encapsular ensamblados, módulos y tipos. Esto nos permite realizar acciones como conocer el nombre de una clase o invocar un método de un objeto -a priori- desconocido en tiempo real.

(más…)

Abstracción de datos (III): Utilizando Reflection para implementar un DAO en C#


El siguiente paso de nuestro objetivo de lograr un nivel de abstracción decente a la hora de implementar un modelo de datos consiste en la utilización del espacio de nombres Reflection. En una entrada anterior mostrábamos una función que nos devolvía un diccionario que contenía tanto el nombre como el valor de los filtros de la consulta.
Hoy vamos a ir más allá. Vamos a olvidarnos de la codificación de un DTO concreto: codificaremos un DAO que espere un objeto DTO genérico que cumpla los siguientes requisitos:

  • Cada Propiedad se corresponderá con un campo de la fuente de datos.
  • El nombre de la clase será DTO + Nombre de la tabla

Con estos dos requisitos, podremos codificar un DAO que comienza a abstraerse de los datos en sí, ligándose únicamente a una estructura arquitectónica y a una fuente de datos concreta (SQL Server). El siguiente paso será eliminar la segunda de estas restricciones, como veremos posteriormente.
Es importante partir de la entrada anterior para entender esta entrada, ya que no escribiré todo el código, sino que mostraré únicamente las modificaciones necesarias a los ficheros anteriores para poder entender el ejemplo.
DTOUsuario

La clase DTOUsuario no sufrirá modificación alguna. Esto es, será el DAO el que se adapte para conseguir la abstracción de un DTO en concreto.
DAOGenerico

En lugar de implementar las sentencias simples (SELECT, INSERT, UPDATE, DELETE) en cada uno de los DAO, crearemos una clase base llamada DAOGenerico en la que crearemos estas operaciones comunes. Posteriormente, las clases que hereden de ésta implementarán métodos específicos para cada entidad.
El primer cambio que tenemos en esta clase es la presencia de las funciones ObtenerElementos (visto en el primer post dedicado a Reflection) y ObtenerOrdenSql (vista en la anterior parte). Codificadas ambas funciones (manteniéndolas intactas), veremos cómo cambiamos la función select. El cuerpo de la misma será el siguiente:

public DataSet select(object datos)

Vemos que ya no esperamos un DTOUsuario, sino que nos basta un objeto cualquiera.

El siguiente paso será declarar los mismos elementos que en la versión anterior, así como comprobar que el objeto recibido no es nulo.

 // Instanciamos un DataSet, que albergará el contenido de la consulta
 DataSet ds = new DataSet();

// Declaramos dos StringBuilders: uno para la sentencia y otra para los campos
 // A su vez, declaramos un ArrayList para almacenar los parámetros.
 StringBuilder SQLString = new StringBuilder();
 StringBuilder Campos = new StringBuilder();
 ArrayList Parametros = new ArrayList();
 String NombreTabla = "";

 // Comprobamos que el DTO exista
 if (datos == null)
 throw new NullReferenceException("GenericDAO.select(datos)");

A continuación, recorremos los elementos del DTO, añadiendo los filtros en el caso de que éstos existan. Recordemos que Reflection nos proporcionaba la funcionalidad necesaria para almacenar en un diccionario el nombre y el valor de cada propiedad del objeto.

 Dictionary coleccionDatos = new Dictionary();
 // Obtenemos todas las propiedades del objeto
 coleccionDatos = ObtenerElementos(datos, MemberTypes.Property);

Ahora iteraremos cada elemento del diccionario, añadiendo, como hacíamos antes, elementos del tipo “Filtro = @Filtro”, solo que ahora el nombre del filtro viene dado por la clave del diccionario y su valor, por el contenido de el elemento que corresponde a esa clave.

 if (coleccionDatos != null)
 {
 foreach (string key in coleccionDatos.Keys)
 {
 if (coleccionDatos[key] != null)
 {
 Campos.Append(key).Append(" = @").Append(key).Append(" AND ");
 Parametros.Add(new SqlParameter("@" + key, coleccionDatos[key]));
 }
 }
 }

Como última novedad, obtendremos el nombre de la tabla a partir del nombre de la clase, eliminando del principio el prefijo “DTO”.

 // Creamos la cláusula SELECT
 SQLString.Append("SELECT * FROM ").Append((datos.GetType()).Name.TrimStart("DTO".ToCharArray()));

El resto del método es igual a la versión anterior.

 // Añadimos la cláusula WHERE, en caso de que sea necesaria
 if (Campos.Length > 0)
 {
 SQLString.Append(" WHERE ");

 // Como la cadena acabará en 'AND ', eliminamos los últimos 4 caracteres para cerrar la sentencia
 SQLString.Append(Campos.ToString().Substring(0, Campos.ToString().Length - 4));
 }

 // Obtenemos un SqlCommand configurado al efecto
 SqlCommand orden = ObtenerOrdenSql(SQLString.ToString(), Parametros);

 // Instanciamos un SqlDataAdapter y efectuamos la consulta
 SqlDataAdapter da = new SqlDataAdapter(orden);
 da.Fill(ds);

 // Por último, devolvemos el DataSet
 return ds;

Hecho esto, podemos crear un DTO por cada tabla de la base de datos. Siempre y cuando cumplamos las normas de respetar los nombres de tabla y columnas, esta función devolverá la consulta apropiada a cada entidad DTO llamando a la función select() y pasándole el objeto DTO como parámetro.

Introducción a Reflection: Recorriendo los elementos de un objeto


La reflexión es un proceso por el cual se posibilita observar y modificar la estructura y comportamiento de un objeto. Utilizar reflexión para programar implica un grado de abstracción enorme, y su complejidad es relativamente elevada.
La reflexión es soportada por multitud de lenguajes, y se aprovecha de la posibilidad de tratar a las instrucciones como datos. Generalmente se utiliza con el objetivo de observar o modificar el estado o el comportamiento de un programa en tiempo de ejecución. Es, por tanto, la base de la metaprogramación.
El Framework .NET posee un espacio de nombres dedicado a esta filosofía: System.Reflection. Veremos un pequeño ejemplo de cómo podemos, por ejemplo, obtener información sobre un objeto en concreto, como el nombre de sus elementos y su contenido.

La clase Dictionary

Las colecciones de datos, incluidas en los espacios de nombres System.Collections y System.Collections.Generic nos permiten agrupar datos de forma más eficiente de lo que nos permitiría, por ejemplo, un Array.
Las estructuras de este espacio de nombres más utilizadas suelen ser los Hashtable (pares clave-valor) y los ArrayList (Arrays de objetos heterogéneos). No obstante, y a nivel personal, una de las colecciones de datos que más potentes me parecen es la clase Dictionary.
Esta clase, al igual que el Hashtable, permite almacenar pares clave-valor. La diferencia está en que, a diferencia del Hashtable, la clave puede ser del tipo que nosotros escojamos (un entero, una estructura, o incluso una clase). Además, es posible acceder tanto a la lista de claves como a la de valores. Por tanto, al crear un diccionario le proporcionaremos la información del tipo de la clave y el tipo del valor.
Para nuestro ejemplo, crearemos un diccionario de tipo (string, object), es decir, un diccionario en el que la clave sea una cadena de texto y el valor sea un objeto.

Obteniendo información sobre un objeto.

Conociendo la clase Dictionary, es hora de declarar el cuerpo de la función que vamos a codificar. Se tratará de una función que recibirá dos parámetros: un objeto y un MemberType. El MemberType no es más que el tipo de miembro que queremos obtener del objeto (método, propiedad, etc.). Por lo tanto, el cuerpo de nuestra función será el siguiente:

private Dictionary<string , object> ObtenerElementos(object objeto, MemberTypes TipoElemento)
{
}

No debemos olvidarnos, eso sí, de incluir los espacios de nombres System.Reflection y System.Collections.Generic para poder hacer uso de la clase Dictionary y de Reflection, respectivamente.
Comenzaremos por declarar un diccionario de tipo (string, object).

Dictionary<string , object> Elementos = new Dictionary<string , object>();

A continuación, recorreremos la información de cada elemento del objeto con un bucle foreach. Para ello utilizaremos la clase MemberInfo.

foreach (MemberInfo infoMiembro in objeto.GetType().GetMembers())
{
}

En el interior del bucle, comprobaremos si el tipo del objeto es el que nosotros buscamos, y si es así, lo insertaremos en nuestro diccionario.

if (infoMiembro.MemberType == TipoElemento)
{
   if ((PropertyInfo)infoMiembro != null)
     Elementos.Add(((PropertyInfo)infoMiembro).Name, ((PropertyInfo)infoMiembro).GetValue(objeto, null));
}

Como podemos ver, el tipo de miembro puede ser de varios tipos.

090509MemberTypes
Finalmente, el diccionario devolverá los nombres de los elementos junto a sus respectivos valores. Por ejemplo, si buscamos las propiedades del objeto, le pasaríamos a la función como segundo parámetro MemberTypes.Property. Si el objeto tuviera una propiedad llamada “IdUsuario” cuyo valor fuese 1, el diccionario guardaría una entrada (“IdUsuario”, (object)1).

El método completo sería el siguiente:

 /// <summary>
 /// Función encargada de devolver un tipo de elemento en concreto de un objeto en un conjunto
 /// de pares clave - valor.
 /// </summary>
 /// <changelog>
 /// Daniel García    15/05/2009    Creación
 /// </changelog>
 private Dictionary<string , object> ObtenerElementos(object objeto, MemberTypes TipoElemento)
 {
   try
   {
     // Declaramos un Diccionario que contendra el nombre de los elementos del objeto y el
     //contenido de cada elemento.
     Dictionary<string , object> Elementos = new Dictionary<string , object>();

     // Se recorren los miembros del objeto
     foreach (MemberInfo infoMiembro in objeto.GetType().GetMembers())
     {
       // Si el tipo del objeto es del tipo que buscamos, se añade al diccionario
       if (infoMiembro.MemberType == TipoElemento)
       {
         if ((PropertyInfo)infoMiembro != null)
           Elementos.Add(((PropertyInfo)infoMiembro).Name, ((PropertyInfo)infoMiembro).GetValue(objeto, null));
       }
     }
     return Elementos;
   }
   catch (Exception ex)
   {
   throw (ex);
   }
 }

Posteriormente encontraremos una utilidad práctica a esta función, aplicándola a nuestro patrón DAO para no tener que depender de los nombres de los campos. Esta sencilla función ayudará a aumentar muchísimo el nivel de abstracción e independencia de nuestra aplicación.

 

ACTUALIZACIÓN: 12/11/2013

Podemos realizar esta última operación mediante LINQ de una forma aún más sencilla.


        private static Dictionary<string, object> ObtenerElementosLinq(object objeto, MemberTypes TipoElemento)
        {
            try
            {
                // Declaramos un Diccionario que contendra el nombre de los elementos del objeto y el
                //contenido de cada elemento.
                Dictionary<string, object> Elementos = new Dictionary<string, object>();

                // Usamos LINQ para realizar la consulta
                var consulta = from i in objeto.GetType().GetMembers()
                                where ((i.MemberType == TipoElemento) && (i != null))
                                select new KeyValuePair<string,object>(((PropertyInfo)i).Name,
                                                        ((PropertyInfo)i).GetValue(objeto, null));

                // Recorremos la lista de pares clave-valor forzando su ejecución
                foreach (KeyValuePair<string, object> kvp in consulta)
                    Elementos.Add(kvp.Key, kvp.Value);

                // Devolemos el diccionario
                return Elementos;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }