Comenzando con LINQ: Bases y Consulta de Datos en Memoria!

Como os comentaba en un post previo, uno de los cometidos como integrantes de un centro de innovación de Microsoft es estar al día de las últimas novedades en las que Microsoft está trabajando, conocer en detalle dichas novedades, cacharrear con ellas, etc, siempre teniendo en mente que resulta realmente complicado seguir el ritmo de creación de Microsoft. Por este motivo, nos planteamos mes a mes que tecnologías sería interesante conocer para transmitir el conocimiento a las empresas de Cantabria y ponerlas en la parrilla de salida hacía las futuras tendencias tecnológicas. Pues bien, una de las últimas tecnologías que hemos estado probando es LINQ, y francamente estoy sorprendido tanto por sus características (construida sobre las innovaciones de lenguaje de C# y VB 9.0) como por sus prestaciones: habilitar el uso de consultas a objetos de memoria, orígenes de datos, XML o cualquier otro “ente” que se nos ocurra (a partir de la capacidad de extensibilidad de LINQ).


Para los nuevos en la materia, LINQ o Language Integrated Query es un framework de consultas que habilita el uso de órdenes tipo SQL integradas en el lenguaje de programación a partir de una serie de operadores estándar de consulta que permiten realizar consultas sobre cualquier colección de tipo IEnumerable<T>. La arquitectura de LINQ es la siguiente:



¿Qué proporciona LINQ?


Antes de entrar en materia, no está de más tener claro que ventajas o características nos da LINQ. Destacaría las siguientes:


·         Un framework unificado de acceso a objetos, datos y XML. Y que además es completamente extensible.


·         Consultas tipo SQL en código C# y VB:


o   Sintaxis potente, similar a T-SQL.


o   La curva de aprendizaje es reducida.


·         Comprobación de tipos e intellisense.


·         Modelo de proveedores extensible, ya se han definido extensiones hacia SQL (LINQ To SQL) y XML (LINQ to XML).


·         Una serie de operadores estándar de consulta out-of-the-box, que permiten definir operaciones de recorrido, filtro y proyección de modo declarativo en el lenguaje.


·         ¿Se os ocurren más?


Bueno, después de esta pequeña introducción a LINQ (para más información, os recomiendo que vayáis al sitio oficial del proyecto LINQ), es hora de pasar a la acción. También es recomendable la información y ejemplos que en el sitio de El Guille han colgado tanto Guillermo Som como Octavio Hernández  En este post comentaré algunas de las innovaciones del lenguaje (para C#) conocidas, así como el modo de definir consultas integradas en el lenguaje contra un objeto de memoria.


C# 3.0: Innovaciones en el lenguaje


Como comentábamos, LINQ se basa en muchas de las innovaciones del lenguaje que viene con C# 3.0 y VB 9.0. Antes de empezar con las innovaciones, seguro que alguno os está preguntando que se necesita para poder probarlas:


·         Evidentemente, Visual Studio 2005 (No necesitáis bajaros la última CTP de Orcas).


·         LINQ Preview para VB y C# de Mayo de 2006 (no he encontrado una versión posterior para poder utilizarla con VS 2005, pero para el propósito de este post es suficiente), que os podéis descargar en este enlace. Al instalar esta Preview, veréis que además de las correspondientes plantillas de proyecto, se instalarán un montón de ejemplos y HandOn labs tanto para VB como C#.


Una vez instalada la preview, si os vais a VS 2005 veréis que aparece una nueva entrada en los tipos de proyectos: LINQ Preview con las correspondientes plantillas añadidas.



Tras pulsar OK, nos saldrá un mensaje advirtiéndonos que este tipo de proyecto se refiere a una versión no soportada de C# 3.0 y que ciertas funcionalidades podrían no funcionar de manera correcta. Corremos el riesgo, y ya estamos listos para empezar a ver las innovaciones en el lenguaje, las cuales están disponibles (no tenéis más que examinar las referencias que se añaden al proyecto)  a través de los siguientes espacios de nombres:


·         using System.Query;


·         using System.Xml.XLinq;


·         using System.Data.DLinq;


·         using System.Expressions; (está la tenemos que añadir para poder crear árboles de expresión como luego veremos).


Entre estas innovaciones son destacables las siguientes


·         Declaración implícita de tipos, lo que implica que cuando declaramos un objeto no es necesario que especifiquemos su tipo, sino que el compilador es capaz de inferirlo de manera dinámica en tiempo de compilación a partir del valor estático asignado. La clave de la declaración de tipos implícitos está en el uso de la palabra reservada var. En la práctica, esta innovación facilita poder referenciar en nuestro código instancias de tipos anónimos. Veamos un ejemplo de declaración implícita de tipos.







            //Declaración ímplicita de tipos


            var numero=2;


            var cadena=”Hola mundo desde LINQ!”;


 


            Console.WriteLine(“numero es de tipo ” + numero.GetType()


                                + “.El valor de numero es ” + numero);


            Console.WriteLine(“cadena es de tipo ” + cadena.GetType()


                                + “.El valor de cadena es ” + cadena);


            Console.ReadLine();


 La salida por pantalla que obtendríamos para este caso es:



La ventaja que nos aporta esta innovación es que libera al desarrollador de tener que definir de manera explícita los tipos que necesite  al programar una aplicación. Esta característica es clave para LINQ, puesto que el desarrollador no necesita definir los tipos que va a devolver una consulta, sino que es el compilador quien dinámicamente va a realizar esta tarea.


·         Tipos anónimos, esta característica permite crear objetos de tipo anónimo, es decir, de nuevo podemos omitir el tipo cuando instanciamos un objeto y este es construido de manera dinámica en tiempo de compilación. Además de poder definir un objeto de manera anónima, es posible definir propiedades asociadas al mismo. Un ejemplo de esta innovación es el siguiente:







            var Santander=new {NombreCiudad=”Santander”, PoblacionCiudad=180000};


            Console.WriteLine(“La ciudad de ” + Santander.NombreCiudad + ” tiene una población de ” +


                                Santander.PoblacionCiudad + ” habitantes”);


            Console.ReadLine();


            Console.WriteLine(“Santander es de tipo ” + Santander.GetType());


            Console.ReadLine();


 En este caso, la salida por pantalla es la siguiente:



Como vemos, aunque hemos declarado un tipo anónimo, el compilador es capaz de inferir el tipo correspondiente y sacar por pantalla los valores de sus propiedades. También vemos que el tipo devuelto es <Projection>f __0 que es el que se infiere en tiempo de compilación.  Si miramos el ensamblado a través de la utilidad reflector, tendremos más información respecto al tipo generado en tiempo de compilación.



 


·         Inicializadores de objetos, esta innovación ya la vimos en la anterior. Lo que permite es inicializar objetos (anónimos o no) en el momento en que los instanciamos.







public class Ciudad


        {


            public string NombreCiudad;


            public int PoblacionCiudad;


        }


        static void Main(string[] args)


        {


 


            Var Santander=new Ciudad{NombreCiudad=”Santander”,


                                        PoblacionCiudad=180000};


            Console.WriteLine(“La ciudad de ” + Santander.NombreCiudad +


                                               ” tiene una población de ” +


                                                Santander.PoblacionCiudad + ” habitantes”);


            Console.ReadLine();


            Console.WriteLine(“Santander es de tipo ” + Santander.GetType());


            Console.ReadLine();


        }




En este caso, en el código estamos combinando la declaración implícita de tipos y la inicialización de objetos. Por lo que ahora, el tipo devuelto es Ciudad.


·         Métodos de extensión, o dicho de otra forma: mecanismos que habilitan la extensión de objetos. La base de esta innovación consiste en crear clases de extensión que nos permitan extender la funcionalidad de tipos existentes a partir de crear nuevos métodos. Los métodos de extensión son métodos estáticos que son  habilitados como métodos de extensión a través de la palabra reservada this.


 






 static class ExtensionDeTipos


        {


            public static string Saludar(this string nombre)


            {


                return (“Hola ” + nombre + “!”);


            }


 


 


        }


        static void Main(string[] args)


        {


           


            string nombre = “Mundo”;


            Console.WriteLine(nombre.Saludar());


            Console.ReadLine();


 


        }


 



Mediante el código anterior, hemos definido una clase estática dentro de la cuál definiremos nuestros métodos de extensión. En este caso, estamos extendiendo nombre que es de tipo cadena mediante el método Saludar y como vemos la clave de esta extensibilidad es la palabra reservada this.



Una característica interesante de los métodos de extensión es que se pueden añadir a cualquier tipo, incluyendo tipos genéricos como List <T> y Dictionary <T>.


 


·         Expresiones lambda, que habilitan el uso de condiciones sin tener que especificar el tipo. En la práctica, las expresiones lambda permiten definir de un modo más claro y directo los métodos anónimos de C # 2.0 (quizás esta es una de las innovaciones más destacadas de C# 3.0 y una de las bases de LINQ junto con los métodos de extensión). Para entender esta innovación, no está de más recordar que es un método anónimo en C# 2.0:







List <string> NombresLongitudMayor5=nombres.FindAll(delegate(string s)


                                                                       {return(s.Length)>=5;});


       


Pues bien, la expresión anterior se puede escribir de manera más directa en C# 3.0 utilizando una expresión lambda, que unida a otras innovaciones simplifica mucho el código anterior







var NombresLongitudMayor5=nombres.FindAll(s => s.Length>=5);


 


Como vemos, una expression lamba consta de tres elementos:


o   Un parámetro de lista (s), que puede ser tipado de manera explícita o implícita. En el ejemplo, de nuevo estamos utilizando tipado implícito.


o   El token =>.


o   La expresión a aplicar (la longitud sea 10).


Un ejemplo completo de uso de expresiones lambda sería el siguiente:







var nombres=new List<string>();


            nombres.Add(“Luis”);


            nombres.Add(“Juan Carlos”);


            nombres.Add(“Pepe”);


            nombres.Add(“Ramón”);


 


            var NombresLongitudMayor5=nombres.FindAll(s => s.Length>=5);


            foreach(string nombre in NombresLongitudMayor5)


            {


                Console.WriteLine(nombre);


            }


            Console.ReadLine();


 


·         Árboles de expresión, que son representaciones más eficientes en memoria de una expresión lambda. La idea de los árboles de expresión es dar una visión más transparente e implícita que el código IL, además de habilitar la utilización de expresiones lambda como datos en tiempo de ejecución. La clave de la definición de árboles de expresión está en un nuevo tipo: Expression <T>. Veamos un ejemplo:







Expression<Func<string,bool>> NombresLongitudMayor5= s => s.Length>=5;


 Console.WriteLine(NombresLongitudMayor5);


 Console.ReadLine();


         Código que produce la siguiente salida:




Como vemos, el árbol de expresión nos permite traducir la expresión lambda a datos que son manipulables. Pero, ¿Para qué nos sirven los árboles de expresión? Pues, y como se comenta en la documentación disponible del proyecto LINQ, para una implementación de acceso a base de datos, nos permitiría traducir los árboles a sentencias apropiadas para un tipo particular de BD. De hecho, esto es lo que se hace en LINQ to SQL (anteriormente DLinq), en el que los árboles de expresión son traducidos a sentencias T-SQL que puedan ser evaluadas en la BD.


LINQ to Objetcs: Consultas a objetos de memoria


Después de haber introducido algunas de las novedades del lenguaje que vendrán con C# 3.0 y de modo equivalente con VB y sobre las que se apoya LINQ, ya es hora de ver las posibilidades de LINQ en la práctica. Básicamente, LINQ utiliza estas innovaciones para definir habilitar el uso de consultas integradas en el lenguaje de programación. Para ello define una serie de operadores estándares de consulta que son métodos de extensión definidos sobre IEnumerable <T>, y cuyas características más relevantes son las siguientes:


·         Están construidos a partir de las innovaciones en el lenguaje vistas.


·         Permiten trabajar con datos en memoria. Esto es especialmente interesante para cuando trabajemos contra datos de una BD, de un archivo XML o cualquier otro origen de datos.


·         Las operaciones siempre se realizan en memoria.


Aquí os dejo el listado de los operadores estándar de consulta. Para conocer en detalle dichos operadores, os recomiendo la lectura de la traducción por Octavio Hernández del documento The LINQ Proyect. Este documento junto con otros también traducidos al castellano lo podéis encontrar aquí.












































Tipo de Operador


Operador


Restricción


Where


Proyección


Select, SelectMany


Ordenación


OrderBy, ThenBy


Agrupamiento


GroupBy, Reverse, GroupJoin


Cuantificadores


Any, All


Particionado


Take, Skip, TakeWhile, SkipWhile


Conjuntos


Distinct, Union, Intersect, Except


Elementos


First, FirstOrDefault, ElementAt


Agregación


Aggregate,Count, Sum, Min, Max, Average


Conversión


ToArray, ToList, ToDictionary


Casting


OfType<T>


Join


Join, GroupJoin, On


 


Además del uso de los operadores estándar de consultas, LINQ utiliza otra innovación conocida como la evaluación diferida de consultas, que implica que una consulta no es realmente evaluada hasta el momento en que se itera con ella. La ventaja de esta técnica es que las consultas van a poder ser evaluadas múltiples veces.


Sin más, vamos a ver un ejemplo de LINQ contra objetos. El código sería el siguiente: 






        //Clase a partir de la cuál definiremos el objeto en memoria a consultar


        public class Ciudad


        {


            public string NombreCiudad;


            public int PoblacionCiudad;


 


        }


        static void Main(string[] args)


        {


         //Objeto en memoria sobre el que vamos a habiltiar la consulta


         var ciudades=new List<Ciudad>{


                        {NombreCiudad=”Santander”,PoblacionCiudad=180000},


                        {NombreCiudad=”León”,PoblacionCiudad=150000},


                        {NombreCiudad=”Ponferrada”,PoblacionCiudad=75000},


                        {NombreCiudad=”Madrid”,PoblacionCiudad=4000000}


         };


        //Consulta…


        var consulta=


                from c in ciudades


                where c.PoblacionCiudad>150000


                select new {c.NombreCiudad,c.PoblacionCiudad};


 


       Console.WriteLine(“Ciudades con más de 150000 habitantes: “);


      //Aquí es dónde realmente se evalua la consulta…


        foreach(var c in consulta)


        {


            Console.WriteLine(“Ciudad: {0}, Población {1}”,


                            c.NombreCiudad,c.PoblacionCiudad);


        }


 


        Console.ReadLine();


 


En el código anterior hemos utilizado varias de las innovaciones vistas, a ver quién me las dice a partir de leer el post (así podré medir si me explico bien o no :P). La salida por pantalla sería la siguiente:



Bueno, pues hasta aquí el primer post que desde el CIIN hacemos de LINQ. Espero vuestros comentarios y respuestas a la dudilla planteada.

Publicado por

Juan Carlos González

Juan Carlos es Ingeniero de Telecomunicaciones por la Universidad de Valladolid y Diplomado en Ciencias Empresariales por la Universidad Oberta de Catalunya (UOC). Cuenta con más de 12 años de experiencia en tecnologías y plataformas de Microsoft diversas (SQL Server, Visual Studio, .NET Framework, etc.), aunque su trabajo diario gira en torno a SharePoint & Office 365. Juan Carlos es MVP de Office Servers & Services desde 2015 (anteriormente fue reconocido por Microsoft como MVP de Office 365 y MVP de SharePoint Server desde 2008 hasta 2015), coordinador del grupo de usuarios .NET de Cantabria (Nuberos.Net, www.nuberos.es), co-fundador y coordinador del Grupo de Usuarios de SharePoint de España (SUGES, www.suges.es), así como co-director de la revista gratuita en castellano sobre SharePoint CompartiMOSS (www.compartimoss.com). Hasta la fecha, ha publicado 8 libros sobre SharePoint & Office 365 y varios artículos en castellano y en inglés sobre ambas plataformas.

13 comentarios en “Comenzando con LINQ: Bases y Consulta de Datos en Memoria!”

  1. Hola espi!
    Pues te comento, depende de en que entorno quieras jugar:

    – Las pruebas de este post en concreto las hice con la CTP de LINQ que te puedes instalar sobre Visual Studio 2005. Esta CTP es de mayo de 2006:
    http://www.microsoft.com/downloads/details.aspx?familyid=1e902c21-340c-4d13-9f04-70eb5e3dceea&displaylang=en

    – Bajándote la Beta 2 de Visual Studio 2008 (más recomendable) que ya viene con LINQ plenamete integrado (ya tiene intellicence). Puedes bajarte el instalable e instalarlo en una máquina virtual (esto es lo que he hecho yo) o bien descargarte la máquina virtual que ya trae la instalación:
    http://www.microsoft.com/downloads/details.aspx?familyid=1e902c21-340c-4d13-9f04-70eb5e3dceea&displaylang=en

    Saludos

    JC

  2. Hace una semana inicie con LINQ y todas las novedades de VS2008 y la verdad que nos hacen las cosas mas facil, ahorrandonos el “trabajo sucio” y lo mejor de todo es que nos da mas tiempo para idear mejores soluciones a los problemas comunes y solventar mas rapido los nuevos.. pronot pondre en mi espacio toas mis aventuras con VS2008.

    Saludos!

  3. Hola Richard!
    Estupendo…chequearé tu blog para ver que novedades y cosas interesantes de VS 2008 nos cuentas…yo también publicaré alguna cosilla (ya he empezado hoy con workflows y Sharepoint).

    Saludos

    JC

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *