Вы находитесь на странице: 1из 22

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

El proyecto LINQ
Mayo de 2006
Publicado: 18 de diciembre de 2006 Traducido por Octavio Hernndez Este artculo no ha sido traducido por Microsoft Este artculo y la veracidad de su traduccin no ha sido revisado o verificado por Microsoft. Microsoft no acepta responsabilidades sobre la veracidad o la informacin de este artculo que se proporciona tal cual por la comunidad.
En esta pgina

Consultas integradas en los lenguajes .NET Introduccin a los operadores de consulta estndar Caractersticas del lenguaje que dan soporte al Proyecto LINQ Expresiones lambda y rboles de expresiones Mtodos extensores Evaluacin diferida de consultas La interfaz IQueryable-TInicializacin de valores compuestos Valores y tipos estructurados Ms operadores de consulta estndar Ordenacin y agrupacin Select vs. SelectMany Operadores de acumulacin Operadores de encuentro Sintaxis de consultas DLinq: Integracin de SQL XLinq: Integracin de XML Conclusin

Consultas integradas en los lenguajes .NET


Despus de dos dcadas, la industria ha alcanzado un punto estable en la evolucin de las tecnologas de programacin orientada a objetos. Los programadores ahora estn familiarizados con conceptos como las clases, objetos y mtodos. Analizando la generacin de tecnologas actual y siguiente, se hace evidente que el siguiente gran reto para la tecnologa de la programacin es reducir la complejidad del acceso e integrar la informacin que no se define de manera nativa utilizando la tecnologa orientada a objetos. Las dos fuentes de informacin no orientadas a objetos ms comunes son las bases de datos relacionales y XML. En vez de aadir caractersticas especficas para el tratamiento de datos relacionales o XML a nuestros lenguajes de programacin y motor de ejecucin, con el proyecto LINQ hemos seguido un enfoque ms general, y estamos aadiendo a .NET Framework facilidades de consulta de propsito general aplicables a todas las fuentes de informacin, y no solo a los datos relacionales o XML. Esta facilidad se llama Consultas integradas en los lenguajes (Language Integrated Query - LINQ). Utilizamos el trmino consultas integradas en los lenguajes para indicar que las consultas son una caracterstica integrada del lenguaje de programacin principal del desarrollador (por ejemplo C#, Visual Basic). Las consultas integradas en los lenguajes permiten que las expresiones de consulta se beneficien de los metadatos ricos, verificacin de sintaxis en tiempo de compilacin, tipado esttico y ayuda

1 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

IntelliSense que antes estaban disponibles solo para el cdigo imperativo. Las consultas integradas en los lenguajes tambin hacen posible aplicar una nica facilidad declarativa de propsito general a toda la informacin en memoria, y no solo a la informacin proveniente de fuentes externas. Las consultas integradas en los lenguajes .NET definen un conjunto de operadores de consulta estndar de propsito general que hacen posible que las operaciones de recorrido, filtro y proyeccin sean expresadas de una manera directa pero declarativa en cualquier lenguaje de programacin. Los operadores de consulta estndar permiten aplicar las consultas a cualquier fuente de informacin basada en IEnumerable<T>. LINQ permite que terceros fabricantes aumenten el conjunto de operadores de consulta estndar, aadiendo los operadores de dominio especfico que sean apropiados para el dominio o la tecnologa de destino. Ms importante an es que terceros fabricantes tambin pueden reemplazar los operadores de consulta estndar con sus propias implementaciones que ofrezcan servicios adicionales como la evaluacin remota, traduccin de consultas, optimizacin, etc. Al adherirse a los convenios del patrn LINQ, tales implementaciones gozarn de la misma integracin en los lenguajes y soporte de herramientas que los operadores de consulta estndar. La extensibilidad de la arquitectura de consultas es aprovechada por el propio proyecto LINQ para ofrecer implementaciones que operan sobre datos XML y SQL. Los operadores de consulta sobre XML (XLinq) utilizan una facilidad de XML en memoria interna eficiente y fcil de usar para ofrecer funcionalidad XPath/XQuery dentro del lenguaje de programacin husped. Los operadores de consulta sobre datos relacionales (DLinq) se apoyan en la integracin de definiciones de esquemas basadas en SQL en el sistema de tipos del CLR. Esta integracin ofrece un fuerte control de tipos sobre los datos relacionales, a la vez que mantiene la potencia expresiva del modelo relacional y el rendimiento de la evaluacin de las consultas directamente en el almacn de datos subyacente. Principio de la pgina

Introduccin a los operadores de consulta estndar


Para ver las consultas integradas en los lenguajes en accin, comenzaremos con un sencillo programa en C# 3.0 que utiliza los operadores de consulta estndar para procesar el contenido de un array:
using System; using System.Query; using System.Collections.Generic; class app { static void Main() { string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; IEnumerable<string> expr = from s in names where s.Length == 5 orderby s select s.ToUpper(); foreach (string item in expr) Console.WriteLine(item); } }

Si se compila y se ejecuta este programa, se obtendr la siguiente salida:


BURKE DAVID FRANK

Para comprender cmo funcionan las consultas integradas en los lenguajes, debemos analizar la primera sentencia de nuestro programa.
IEnumerable<string> expr = from s in names where s.Length == 5 orderby s select s.ToUpper();

2 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

La variable local expr es inicializada con una expresin de consulta. Una expresin de consulta opera sobre una o ms fuentes de informacin aplicando uno o ms operadores de consulta, ya sean operadores de consulta estndar u operadores especficos de un dominio. Esta expresin utiliza tres de los operadores de consulta estndar: Where, OrderBy y Select. Visual Basic 9.0 soporta igualmente LINQ. He aqu la sentencia anterior escrita en Visual Basic 9.0:
Dim expr As IEnumerable(Of String) = From s in names _ Where s.Length = 5 _ Order By s _ Select s.ToUpper()

Tanto la sentencia de C# como la de Visual Basic mostradas aqu utilizan la sintaxis de consultas. Del mismo modo que la sentencia foreach, la sintaxis de consultas es una notacin declarativa conveniente ms concisa que el cdigo que se podra escribir manualmente. Las sentencias anteriores son semnticamente idnticas a la siguiente sintaxis explcita mostrada en C#:
IEnumerable<string> expr = names .Where(s => s.Length == 5) .OrderBy(s => s) .Select(s => s.ToUpper());

Los argumentos de los operadores Where, OrderBy y Select se conocen como expresiones lambda, que son fragmentos de cdigo muy similares a los delegados. Ellas permiten que los operadores de consulta estndar se definan individualmente como mtodos y se conecten utilizando notacin de punto. Conjuntamente, estos mtodos conforman las bases de un lenguaje de consultas extensible. Principio de la pgina

Caractersticas del lenguaje que dan soporte al Proyecto LINQ


LINQ est construido enteramente sobre caractersticas de los lenguajes de propsito general, algunas de las cuales son nuevas en C# 3.0 y Visual Basic 9.0. Cada una de estas caractersticas tiene su utilidad propia, pero conjuntamente, estas caractersticas ofrecen una manera extensible de definir consultas y API de consultas. En esta seccin exploraremos estas caractersticas de los lenguajes y cmo contribuyen a un estilo de consultas mucho ms directo y declarativo. Principio de la pgina

Expresiones lambda y rboles de expresiones


Muchos operadores de consulta permiten al usuario suministrar una funcin que realice un filtrado, proyeccin o extraccin de clave. Las facilidades de consulta se apoyan en el concepto de las expresiones lambda, que ofrecen a los desarrolladores una manera conveniente de escribir funciones que pueden ser pasadas como argumentos para su evaluacin subsiguiente. Las expresiones lambda son similares a los delegados del CLR y deben adherirse a una firma de mtodo definida por un tipo delegado. Para ilustrar esto, podemos expandir la sentencia anterior a otra forma equivalente pero ms explcita usando el tipo delegado Func:
Func<string, bool> filter = s => s.Length == 5; Func<string, string> extract = s => s; Func<string, string> project = s => s.ToUpper(); IEnumerable<string> expr = names.Where(filter) .OrderBy(extract) .Select(project);

Las expresiones lambda son la evolucin natural de los mtodos annimos de C# 2.0. Por ejemplo, podramos haber escrito el ejemplo usando mtodos annimos de la siguiente forma:
Func<string, bool> filter = delegate (string s) { return s.Length == 5; };

Func<string, string> extract = delegate (string s) {

3 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

return s; }; Func<string, string> project = delegate (string s) { return s.ToUpper(); }; IEnumerable<string> expr = names.Where(filter) .OrderBy(extract) .Select(project);

En general, el desarrollador es libre de utilizar mtodos nombrados, mtodos annimos o expresiones lambda con los operadores de consulta. Las expresiones lambda tienen la ventaja de ofrecer la sintaxis ms directa y compacta para la escritura. Y lo que es ms importante, las expresiones lambda pueden ser compiladas como cdigo o como datos, lo que permite que las expresiones lambda puedan ser tratadas en tiempo de ejecucin por optimizadores, traductores y evaluadores. LINQ define un tipo distinguido, Expression<T> (en el espacio de nombres System.Expressions), que indica que se desea obtener un rbol de expresin para una expresin lambda dada en vez de un cuerpo de mtodo tradicional basado en IL. Los rboles de expresiones son representaciones eficientes en memoria de las expresiones lambda y hacen la estructura de las expresiones transparente y explcita. La determinacin de si el compilador emitir cdigo IL ejecutable o un rbol de expresin viene dada por cmo se utiliza la expresin lambda. Cuando una expresin lambda es asignada a una variable, campo o parmetro cuyo tipo es un delegado, el compilador emite cdigo IL que es idntico al de un mtodo annimo. Cuando una expresin lambda es asignada a una variable, campo o parmetro cuyo tipo es Expression<T>, el compilador emite un rbol de expresin. Por ejemplo, considere las dos siguientes declaraciones de variables:
Func<int, bool> f = n => n < 5; Expresin<Func<int, bool>> e = n => n < 5;

La variable f es una referencia a un delegado directamente ejecutable:


bool isSmall = f(2); // isSmall es ahora true

La variable e es una referencia a un rbol de expresin que no es directamente ejecutable:


bool isSmall = e(2); // error de compilacin, expresin == datos

A diferencia de los delegados, que son efectivamente cdigo opaco, podemos interactuar con el rbol de expresin como lo hacemos con cualquier otra estructura de nuestro programa. Por ejemplo, este programa:
Expresin<Func<int, bool>> filter = n => n < 5; BinaryExpression body = (BinaryExpression)filter.Body; ParameterExpression left = (ParameterExpression)body.Left; ConstantExpression right = (ConstantExpression)body.Right; Console.WriteLine("{0} {1} {2}", left.Name, body.NodeType, right.Value);

descompone el rbol de expresin en tiempo de ejecucin e imprime la cadena:


n LT 5

Esta capacidad de tratar las expresiones como datos en tiempo de ejecucin es crtica para hacer posible un ecosistema de libreras de terceros que aprovechen las abstracciones de consulta bsicas que son parte de la plataforma. La implementacin del acceso a datos de DLinq aprovecha esta capacidad para traducir los rboles de expresiones a sentencias T-SQL que pueden ser evaluadas en la base de datos. Principio de la pgina

Mtodos extensores

4 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Las expresiones lambda son una parte importante de la arquitectura de consultas. Los mtodos extensores son otra. Los mtodos extensores combinan la flexibilidad del tipado de pato que han hecho populares los lenguajes dinmicos con el rendimiento y la validacin en tiempo de compilacin de los lenguajes de tipado esttico. Mediante los mtodos extensores, terceros fabricantes pueden aumentar el contrato pblico de un tipo con nuevos mtodos, permitiendo a la vez a autores de tipos individuales ofrecer su propia implementacin especializada de esos mtodos. Los mtodos extensores se definen como mtodos estticos en clases estticas, pero se marcan con el atributo [System.Runtime.CompilerServices.Extension] en los metadatos del CLR. Se propone que los lenguajes ofrezcan una sintaxis directa para los mtodos extensores. En C#, los mtodos extensores se indican mediante el modificador this aplicado al primer parmetro del mtodo extensor. Veamos la definicin del operador de consulta ms sencillo, Where:
namespace System.Query { using System; using System.Collections.Generic; public static class Sequence { public static IEnumerable<T> Where<T>( this IEnumerable<T> source, Func<T, bool> predicate) { foreach (T item in source) if (predicate(item)) yield return item; } } }

El tipo del primer parmetro de un mtodo extensor indica a qu tipo de la extensin se aplica. En el ejemplo anterior, el mtodo extensor Where extiende el tipo IEnumerable<T>. Debido a que Where es un mtodo esttico, podemos llamarlo directamente como a cualquier otro mtodo esttico:
IEnumerable<string> expr = Sequence.Where(names, s => s.Length < 6);

Sin embargo, lo que hace nicos a los mtodos extensores es que tambin pueden ser llamados utilizando sintaxis de instancia:
IEnumerable<string> expr = names.Where(s => s.Length < 6);

Los mtodos extensores se resuelven en tiempo de compilacin sobre la base de qu mtodos extensores estn en mbito. Cuando un espacio de nombres en importado mediante la sentencia using de C# o la sentencia Import de VB, todos los mtodos extensores definidos por las clases estticas de ese espacio de nombres son tradas al mbito actual. Los operadores de consulta estndar se definen como mtodos extensores en el tipo System.Query.Sequence. Al examinar los operadores de consulta estndar, observar que todos excepto unos pocos estn definidos en trminos de la interfaz IEnumerable<T>. Esto significa que cada fuente de informacin compatible con IEnumerable<T> obtiene los operadores de consulta estndar simplemente agregando la siguiente sentencia using en C#:
using System.Query; // hace visibles los operadores de consulta

Los usuarios que quieran sustituir los operadores de consulta estndar para un tipo especfico pueden (a) definir sus propios mtodos con los mismos nombres y firmas compatibles en el tipo especfico o (b) definir nuevos mtodos extensores con los mismos nombres que extiendan el tipo especfico. Los usuarios que quieran esquivar totalmente los operadores de consulta estndar pueden simplemente no poner en mbito System.Query y escribir sus propios mtodos extensores para IEnumerable<T>. Los mtodos extensores reciben la prioridad ms baja en trminos de resolucin, y son utilizados nicamente cuando no se encuentra una coincidencia en el tipo de destino y sus tipos base. Esto permite que los tipos definidos por el usuario ofrezcan sus propios operadores de consulta que tengan precedencia sobre los operadores estndar. Por ejemplo, observe la siguiente coleccin personalizada:

5 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

public class MySequence : IEnumerable<int> { public IEnumerator<int> GetEnumerator() { for (int i = 1; i <= 10; i++) yield return i; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerable<int> Where(Func<int, bool> filter) { for (int i = 1; i <= 10; i++) if (filter(i)) yield return i; } }

Dada la definicin de clase anterior, el siguiente programa:


MySequence s = new MySequence(); foreach (int item in s.Where(n => n > 3)) Console.WriteLine(item);

utilizar la implementacin de MySequence.Where y no el mtodo extensor, ya que los mtodos de instancia tienen precedencia sobre los mtodos extensores. El operador OfType es uno de los pocos operadores de consulta estndar que no extienden una fuente de informacin basada en IEnumerable<T>. Echemos un vistazo al operador de consulta OfType:
public static IEnumerable<T> OfType<T>(this IEnumerable source) { foreach (object item in source) if (item is T) yield return (T)item; }

OfType acepta no solamente fuentes basadas en IEnumerable<T>, sino tambin fuentes programadas contra la versin no parametrizada de la interfaz IEnumerable que estaba disponible en la versin 1 de .NET Framework. El operador OfType permite a los usuarios aplicar los operadores de consulta estndar a colecciones clsicas de .NET como la siguiente:
// "clsica" no puede utilizarse directamente con los // operadores de consulta IEnumerable classic = new OlderCollectionType(); // "moderna" puede utilizarse directamente con los // operadores de consulta IEnumerable<object> modern = classic.OfType<object>();

En este ejemplo, la variable modern produce la misma secuencia de valores que classic, pero su tipo es compatible con el cdigo moderno basado en IEnumerable<T>, incluyendo los operadores de consulta estndar. El operador OfType es tambin til para nuevas fuentes de informacin, ya que permite el filtrado de los valores provenientes de una fuente en base al tipo. Al producir la nueva secuencia, OfType simplemente omite los miembros de la secuencia original que no son compatibles con el argumento de tipo. Analice el sencillo programa que extrae las cadenas de un array heterogneo:
object[] vals = { 1, "Hello", true, "World", 9.1 }; IEnumerable<string> justStrings = vals.OfType<string>();

Al enumerar la variable justStrings en una sentencia foreach, obtendremos la secuencia formada por las dos cadenas Hello y World. Principio de la pgina

Evaluacin diferida de consultas


Los lectores observadores pueden haber notado que el operador estndar Where se implementa utilizando la construccin yield introducida en C# 2.0. Esta tcnica de implementacin es comn para todos los

6 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

operadores estndar que devuelven secuencias de valores. El uso de yield ofrece un beneficio interesante, que consiste en que la consulta no es realmente evaluada hasta que se itera sobre ella, ya sea mediante una sentencia foreach o manualmente, utilizando los mtodos GetEnumerator y MoveNext subyacentes. Esta evaluacin diferida permite que las consultas se mantengan como valores basados IEnumerable<T> que pueden ser evaluados mltiples veces, cada vez produciendo resultados potencialmente diferentes. En muchas aplicaciones, ste es exactamente el comportamiento deseado. Para las aplicaciones que deseen guardar en cach los resultados de la evaluacin de una consulta, se ofrecen dos operadores, ToList y ToArray, que fuerzan la evaluacin inmediata de una consulta y devuelven un objeto List<T> o un array que contiene los resultados de la evaluacin de la consulta. Para ver cmo funciona la evaluacin diferida, analice el siguiente programa, que ejecuta una consulta sencilla sobre un array:
// se declara una variable que contiene varias cadenas string[] names = { "Allen", "Arthur", "Bennett" }; // se declara una variable que representa una consulta IEnumerable<string> ayes = names.Where(s => s[0] == 'A'); // se evala la consulta foreach (string item in ayes) Console.WriteLine(item); // se modifica la fuente de informacin original names[0] = "Bob"; // se evala la consulta otra vez; esta vez no estar "Allen" foreach (string item in ayes) Console.WriteLine(item);

La consulta es evaluada cada vez que se itera sobre la variable ayes. Para indicar que se requiere una copia cacheada de los resultados, debemos simplemente aadir un operador ToList o ToArray a la consulta de la siguiente forma:
// se declara una variable que contiene varias cadenas string[] names = { "Allen", "Arthur", "Bennett" }; // se declara una variable que representa el resultado // de la evaluacin inmediata de una consulta string[] ayes = names.Where(s => s[0] == 'A').ToArray(); // se itera sobre los resultados cacheados foreach (string item in ayes) Console.WriteLine(item); // modificar la fuente original no afecta a ayes names[0] = "Bob"; // se itera sobre el resultado otra vez; an estar "Allen" foreach (string item in ayes) Console.WriteLine(item);

Tanto ToArray como ToList fuerzan la evaluacin inmediata de la consulta. Eso tambin es vlido para los operadores de consulta estndar que devuelven valores nicos (por ejemplo First, ElementAt, Sum, Average, All, Any). Principio de la pgina

La interfaz IQueryable-TEl mismo modelo de ejecucin diferida es generalmente deseado para las fuentes de datos que implementan la funcionalidad de consultas mediante rboles de expresiones, como DLinq. Estas fuentes de datos pueden beneficiarse de implementar la interfaz IQueryable<T>, para la cual todos los operadores de consulta requeridos por el patrn LINQ se implementan utilizando rboles de expresiones. Cada IQueryable<T> tiene una representacin de el cdigo necesario para ejecutar la consulta en la forma de un rbol de expresin. Todos los operadores de consulta diferidos devuelven un nuevo IQueryable que aumenta ese rbol de expresin con una representacin de una llamada a ese operador de consulta. Por

7 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

esta razn, cuando llega el momento de evaluar la consulta, generalmente porque el IQueryable es enumerado, la fuente de datos puede procesar el rbol de expresin que representa a la consulta entera en un solo lote. Por ejemplo, una consulta DLinq compleja obtenida mediante numerosas llamadas a operadores de consulta puede resultar en que una nica consulta SQL sea enviada a la base de datos. La ventaja para los implementadores de fuentes de datos de reutilizar esta funcionalidad diferida implementando la interfaz IQueryable<T> es obvia. Para los clientes que escriben las consultas, por la otra parte, es una gran ventaja tener un tipo comn para las fuentes de informacin remotas. Esto no solo les permite escribir consultas polimrficas que pueden ser utilizadas contra diferentes fuentes de datos, sino que tambin abre la posibilidad de escribir consultas que operen entre diferentes dominios. Principio de la pgina

Inicializacin de valores compuestos


Las expresiones lambda y los mtodos extensores nos ofrecen todo lo que necesitamos para las consultas que simplemente filtran los miembros de una secuencia de valores. La mayora de las expresiones de consulta realizan adems una proyeccin de esos miembros, transformando efectivamente los miembros de la secuencia original en miembros cuyo valor y tipo puede se diferente del original. Para dar soporte a tales transformaciones, LINQ se apoya en una nueva construccin conocida como expresiones de inicializacin de objetos para crear nuevas instancias de tipos estructurados. Para el resto de este documento, asumiremos que se ha definido el siguiente tipo:
public class Person { string name; int age; bool canCode; public string Name { get { return name; } set { name = value; } } public int Age { get { return age; } set { age = value; } } public bool CanCode { get { return canCode; } set { canCode = value; } } }

Las expresiones de inicializacin de objetos nos permiten construir fcilmente valores basados en los campos y propiedades pblicas de un tipo. Por ejemplo, para crear un nuevo valor de tipo Person podemos escribir esta sentencia:
Person value = new Person { Name = "Chris Smith", Age = 31, CanCode = false };

Semnticamente, esta sentencia es equivalente a la siguiente secuencia de sentencias:


Person value = new Person(); value.Name = "Chris Smith"; value.Age = 31; value.CanCode = false;

Las expresiones de inicializacin de objetos son una caracterstica importante para las consultas integradas en los lenguajes, ya que permiten la construccin de nuevos valores estructurados en contextos en los que solo se permiten expresiones (como dentro de expresiones lambda y rboles de expresiones). Por ejemplo, observe esta expresin de consulta que crea un nuevo objeto de tipo Person para cada valor de la secuencia de entrada:
IEnumerable<Person> expr = names.Select(s => new Person { Name = s, Age = 21, CanCode = s.Length == 5 });

La sintaxis de inicializacin de objetos es tambin conveniente para inicializar arrays de valores

8 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

estructurados. Por ejemplo, observe cmo esta variable de tipo array se inicializa mediante inicializadores de objetos individuales:
static Person[] people = { new Person { Name="Allen Frances", new Person { Name="Burke Madison", new Person { Name="Connor Morgan", new Person { Name="David Charles", new Person { Name="Everett Frank", }; Age=11, Age=50, Age=59, Age=33, Age=16, CanCode=false }, CanCode=true }, CanCode=false }, CanCode=true }, CanCode=true },

Principio de la pgina

Valores y tipos estructurados


El Proyecto LINQ soporta un estilo de programacin centrado en datos en el que algunos tipos existen principalmente para suministrar una forma esttica de un valor estructurado en vez de un objeto completo con su estado y comportamiento. Llevando esta premisa a su conclusin lgica, frecuentemente ocurre que al desarrollador le interesa solo la estructura del valor, y la necesidad de un tipo nombrado para esa estructura es de muy poca utilidad. Esto lleva a la introduccin de los tipos annimos, que permiten definir nuevas estructuras en lnea con su inicializacin. En C#, la sintaxis para los tipos annimos es similar a la sintaxis de inicializacin de objetos, con la excepcin de que el nombre del tipo es omitido. Por ejemplo, observe las dos siguientes sentencias:
object v1 = new Person { Name = "Chris Smith", Age = 31, CanCode = false }; object v2 = new { // observe la omisin del nombre del tipo Name = "Chris Smith", Age = 31, CanCode = false };

Las variables v1 y v2 apuntan ambas a un objeto en memoria cuyo tipo del CLR tiene tres propiedades pblicas Name, Age y CanCode. Las variables se diferencian en que v2 hace referencia a una instancia de un tipo annimo. En trminos del CLR, los tipos annimos no son diferentes de cualquier otro tipo. Lo que hace especiales a los tipos annimos es que no tienen un nombre significativo en el lenguaje de programacin la nica manera de crear instancias de un tipo annimo es utilizando la sintaxis mostrada anteriormente. Para permitir que las variables hagan referencia a instancias de tipos annimos y al mismo tiempo se beneficien del tipado esttico, C# introduce la palabra clave var, que puede ser utilizada en lugar del nombre del tipo en las declaraciones de variables locales. Por ejemplo, observe el siguiente programa vlido de C# 3.0:
var s = "Bob"; var n = 32; var b = true;

La palabra clave var indica al compilador que deduzca (infiera) el tipo de la variable a partir del tipo esttico de la expresin utilizada para inicializar la variable. En el ejemplo, los tipos de s, n y b son string, int y bool, respectivamente. Este programa es idntico al siguiente:
string s = "Bob"; int n = 32; bool b = true;

La palabra clave var es solo un mecanismo de conveniencia en el caso de variables cuyos tipos tienen nombres significativos, pero una necesidad en el caso de variables que hacen referencia a instancias de tipos annimos.
var value = new { Name = "Chris Smith", Age = 31, CanCode = false };

En el ejemplo anterior, la variable value es de un tipo annimo cuya definicin es equivalente al siguiente

9 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

seudo-C#:
internal string int bool class ??? { _Name; _Age; _CanCode;

public string Name { get { return _Name; } set { _Name = value; } } public int Age{ get { return _Age; } set { _Age = value; } } public bool CanCode { get { return _CanCode; } set { _CanCode = value; } } public bool Equals(object obj) { } public bool GetHashCode() { } }

Los tipos annimos no pueden ser compartidos a travs de las fronteras de ensamblados; sin embargo, el compilador garantiza que existir a lo sumo un tipo annimo para cada secuencia diferente de pares nombre/tipo de propiedad dentro de cada ensamblado. Debido a que los tipos annimos se utilizan frecuentemente en proyecciones para seleccionar uno o ms miembros de un valor estructurado existente, podemos simplemente hacer referencia a los campos o propiedades de otro valor en la inicializacin de un tipo annimo. Esto resulta en que al nuevo tipo se le asocia una propiedad cuyo nombre, tipo y valor son copiados del campo o propiedad referenciada. Por ejemplo, observe este ejemplo, que crea un nuevo valor estructurado combinando las propiedades de otros valores:
var bob = new Person { Name = "Bob", Age = 51, CanCode = true }; var jane = new { Age = 29, FirstName = "Jane" }; var couple = new { Husband = new { bob.Name, bob.Age }, Wife = new { Name = jane.FirstName, jane.Age } }; int ha = couple.Husband.Age; // ha == 51 string wn = couple.Wife.Name; // wn == "Jane"

Las referencias a campos o propiedades mostradas anteriormente son simplemente una sintaxis conveniente para escribir la siguiente forma ms explcita:
var couple = new { Husband = new { Name = bob.Name, Age = bob.Age }, Wife = new { Name = jane.FirstName, Age = jane.Age } };

En ambos casos, la variable couple obtiene su propia copia de las propiedades Name y Age obtenidas de bob y jane.Los tipos annimos se utilizan con mayor frecuencia en la clusula select de una consulta. Por ejemplo, observe la siguiente consulta:
var expr = people.Select(p => new { p.Name, BadCoder = p.Age == 11 }); foreach (var item in expr) Console.WriteLine("{0} es un {1} coder", item.Name, item.BadCoder ? "bad" : "good");

En este ejemplo, hemos sido capaces de crear una nueva proyeccin sobre el tipo Person que coincide con la forma que necesitamos para nuestro tratamiento, manteniendo las ventajas del tipado esttico.

10 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Principio de la pgina

Ms operadores de consulta estndar


Sobre las facilidades de consulta bsicas descritas anteriormente, se ha definido un conjunto de operadores que ofrecen maneras tiles de manipular secuencias y componer consultas, dando al usuario un alto nivel de control sobre el resultado dentro del marco de trabajo conveniente de los operadores de consulta estndar. Principio de la pgina

Ordenacin y agrupacin
En general, la evaluacin de una expresin de consulta da como resultado una secuencia de valores que se producen en un orden que es intrnseco a las fuentes de informacin subyacentes. Para dar a los desarrolladores un control explcito sobre el orden en que los valores son producidos, se han definido operadores de consulta estndar para controlar ese orden. El ms bsico de estos operadores es el operador OrderBy. Los operadores OrderBy y OrderByDescending pueden ser aplicados a cualquier fuente de informacin y permitir al usuario suministrar una funcin de extraccin de clave que produce el valor utilizado para ordenar los resultados. OrderBy y OrderByDescending tambin aceptan una funcin de comparacin opcional que puede ser utilizada para imponer un orden parcial sobre las claves. Veamos un ejemplo bsico:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; // ordenacin de identidad var s1 = names.OrderBy(s => s); var s2 = names.OrderByDescending(s => s); // ordenacin por longitud var s3 = names.OrderBy(s => s.Length); var s4 = names.OrderByDescending(s => s.Length);

Las dos primeras expresiones de consulta producen nuevas secuencias basadas en la ordenacin de los miembros de la fuente en base a la comparacin de cadenas. Las segundas dos consultas producen nuevas secuencias basadas en la ordenacin de los miembros de la fuente segn la longitud de cada cadena. Para permitir mltiples criterios de ordenacin, tanto OrderBy como OrderByDescending devuelven SortedSequence<T> en vez de la interfaz genrica IEnumerable<T>. Solo dos operadores estn definidos sobre el tipo SortedSequence<T>: ThenBy y ThenByDescending, que aplican criterios de ordenacin adicionales (subordinados). Los propios ThenBy/ThenByDescending devuelven SortedSequence<T>, permitiendo la utilizacin de cualquier cantidad de operadores ThenBy/ThenByDescending:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; var s1 = names.OrderBy(s => s.Length).ThenBy(s => s);

La evaluacin de la consulta referenciada mediante s1 en este ejemplo producir la siguiente secuencia de valores:
"Burke", "David", "Frank", "Albert", "Connor", "George", "Harris", "Everett"

Adems de la familia de operadores OrderBy, entre los operadores de consulta estndar tambin se incluye el operador Reverse. Reverse simplemente enumera una secuencia y produce los mismos valores en orden inverso. A diferencia de OrderBy, Reverse no tiene en cuenta los propios valores para determinar el orden, sino que se apoya nicamente en el orden en que los valores son producidos por la fuente subyacente.El operador OrderBy impone una ordenacin sobre una secuencia de valores. Entre los operadores de consulta estndar tambin se incluye el operador GroupBy, que impone la particin en

11 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

grupos de los valores de una secuencia en base a una funcin de extraccin de clave. El operador GroupBy devuelve una secuencia de valores IGrouping, uno para cada valor de clave distinto encontrado. Un IGrouping es un IEnumerable que adicionalmente contiene la clave que fue utilizada para extraer su contenido:
public interface IGrouping<K, T> : IEnumerable<T> { public K Key { get; } }

La aplicacin ms simple de GroupBy es similar a la siguiente:string[] names = { "Albert", "Burke", "Connor", "David",
"Everett", "Frank", "George", "Harris"}; // agrupar por longitud var groups = names.GroupBy(s => s.Length); foreach (IGrouping<int, string> group in groups) { Console.WriteLine("Strings of length {0}", group.Key); foreach (string value in group) Console.WriteLine(" {0}", value); }

Al ser ejecutado, este programa imprime lo siguiente:


Strings of length 6 Albert Connor George Harris Strings of length 5 Burke David Frank Strings of length 7 Everett

Del mismo modo que Select, GroupBy permite suministrar una funcin de proyeccin que ser utilizada para poblar los miembros del grupo.
string[] names = { "Albert", "Burke", "Connor", "David", "Everett", "Frank", "George", "Harris"}; // agrupar por longitud var groups = names.GroupBy(s => s.Length, s => s[0]); foreach (IGrouping<int, char> group in groups) { Console.WriteLine("Strings of length {0}", group.Key); foreach (char value in group) Console.WriteLine(" {0}", value); }

Esta variante imprime lo siguiente:


Strings of length 6 A C G H Strings of length 5 B D F Strings of length 7 E

Observe en este ejemplo que el tipo proyectado no tiene que ser el mismo tipo original. En este caso, hemos creado una agrupacin de enteros a caracteres a partir de una secuencia de cadenas. Principio de la pgina

12 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Select vs. SelectMany


Varios operadores de consulta estndar se han definido para permitir la acumulacin de una secuencia de valores en un nico valor. El operador de acumulacin ms general es Aggregate, definido de la siguiente forma:
public static U Aggregate<T, U>(this IEnumerable<T> source, U seed, Func<U, T, U> func) { U result = seed; foreach (T element in source) result = func(result, element); return result; }

El operador Aggregate simplifica la realizacin de un clculo sobre una secuencia de valores. Aggregate llama a la expresin lambda una vez para cada miembro de la secuencia subyacente. Cada vez que Aggregate llama a la expresin lambda, le pasa el miembro de la secuencia y un valor acumulado (cuyo valor inicial viene dado por el parmetro seed de Aggregate). El resultado de la evaluacin de la expresin lambda sustituye al valor acumulado anterior, y Aggregate devuelve el resultado final de la expresin lambda. Por ejemplo, el siguiente programa utiliza Aggregate para acumular la cantidad general de caracteres en un array de cadenas:
string[] names = { "Albert", "Burke", "Connor", "David", "Everett", "Frank", "George", "Harris"}; int count = names.Aggregate(0, (c, s) => c + s.Length); // count == 46

Adems del operador de propsito general Aggregate, entre los operadores de consulta estndar tambin se incluyen un operador de propsito general Count y cuatro operadores de acumulacin (Min, Max, Sum y Average) que simplifican esas operaciones comunes de acumulacin. Las funciones de acumulacin numricas operan sobre una secuencia de tipos numricos (por ejemplo, int, double, decimal) o sobre secuencias de valores arbitrarios, siempre que se suministre una funcin que proyecte los miembros de la secuencia a un tipo numrico. El siguiente programa ilustra las dos formas del operador Sum antes descritas:
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; string[] names = { "Albert", "Burke", "Connor", "David", "Everett", "Frank", "George", "Harris"}; int total1 = numbers.Sum(); // total1 == 55 int total2 = names.Sum(s => s.Length); // total2 == 46

Observe que la segunda sentencia Sum es equivalente al ejemplo anterior que utilizaba Aggregate. Principio de la pgina

Operadores de acumulacin
El operador Select exige que la funcin de transformacin produzca un valor para cada valor de la secuencia original. Si su funcin de transformacin devuelve un valor que es en s mismo una secuencia, es responsabilidad del consumidor recorrer manualmente las sub-secuencias. Por ejemplo, observe el siguiente programa, que descompone cadenas en palabras utilizando el mtodo String.Split:
string[] text = { "Albert was here", "Burke slept late", "Connor is happy" }; var tokens = text.Select(s => s.Split(' ')); foreach (string[] line in tokens) foreach (string token in line) Console.Write("{0}.", token);

13 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Al ser ejecutado, este programa imprime el siguiente texto: Albert.was.here.Burke.slept.late.Connor.is.happy. Idealmente, nos habra gustado que nuestra consulta hubiera devuelto una secuencia aplanada de palabras y no expusiera el string[] intermedio al consumidor. Para lograr esto, se puede utilizar el operador SelectMany en vez del operador Select. El operador SelectMany funciona de una manera similar al operador Select. Se diferencia en que espera que la funcin de transformacin devuelva una secuencia, que ser entonces expandida por el operador SelectMany. Este es nuestro programa rescrito utilizando SelectMany:
string[] text = { "Albert was here", "Burke slept late", "Connor is happy" }; var tokens = text.SelectMany(s => s.Split(' ')); foreach (string token in tokens) Console.Write("{0}.", token);

La utilizacin de SelectMany provoca que cada secuencia intermedia sea expandida como parte de la evaluacin normal. SelectMany es ideal para combinar dos fuentes de informacin:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; var query = names.SelectMany(n => people.Where(p => n.Equals(p.Name)) );

En la expresin lambda pasada a SelectMany, la consulta anidada se aplica a una fuente de informacin diferente, pero tiene dentro de su mbito al parmetro n pasado de la fuente ms externa. Por lo tanto, people.Where es llamado una vez para cada n, con las secuencias resultantes aplanadas por SelectMany para el resultado final. El resultado es una secuencia de todas las personas cuyo nombre aparece en el array names. Principio de la pgina

Operadores de encuentro
En un programa orientado a objetos, los objetos relacionados con otros estn generalmente enlazados mediante referencias a objetos fciles de navegar. Esto generalmente no se cumple para fuentes de informacin externas, donde los registros de datos frecuentemente no tienen otra opcin que apuntar a otros de manera simblica, mediante claves externas u otros datos que permitan identificar unvocamente a la entidad apuntada. El concepto de encuentros se refiere a la operacin de combinar los elementos de una secuencia con los elementos con los que ellos coinciden de otra secuencia. El ejemplo anterior que utiliza SelectMany hace exactamente eso, buscar coincidencias de cadenas con personas cuyos nombres son esas cadenas. Sin embargo, para este propsito especfico, el enfoque basado en SelectMany no es muy eficiente recorrer todos los elementos de people para todos y cada uno de los elementos de names. Utilizando toda la informacin de este escenario las dos fuentes de informacin y las claves por las que se deben combinar en una nica llamada a mtodo, el operador Join es capaz de hacer un mucho mejor trabajo:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; var query = names.Join(people, n => n, p => p.Name, (n,p) => p);

Esto puede parecer complicado, pero nos permitir ver cmo las piezas encajan: el mtodo Join es aplicado a la fuente de datos externa, names. El primer argumento es la fuente de datos interna, people. El segundo y tercer argumentos son expresiones lambda para extraer claves de los elementos de

14 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

las secuencias externa e interna, respectivamente. Estas claves son las que el mtodo Join utiliza para buscar las coincidencias de elementos. Aqu queremos que los propios nombres coincidan con la propiedad Name de las personas. La expresin lambda final es entonces responsable de producir los elementos de la secuencia resultante: es llamada para cada pareja de elementos coincidentes n y p, y es utilizada para dar forma al resultado. En este caso, hemos elegido descartar n y devolver p. El resultado final es la lista de elementos Person de people cuyo Name est en la lista names. Un pariente ms potente de Join es el operador GroupJoin. GroupJoin se diferencia de Join en el modo en que se utiliza la expresin lambda que da forma al resultado: en vez de ser invocada para cada pareja individual de elementos externo e interno, ser llamada solamente una vez para cada elemento externo, con una secuencia de todos los elementos internos que coinciden con ese elemento externo. Poniendo un ejemplo concreto:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; var query = names.GroupJoin(people, n => n, p => p.Name, (n, matching) => new { Name = n, Count = matching.Count() } );

Esta llamada produce una secuencia de los nombres iniciales emparejados con la cantidad de personas que tiene ese nombre. Por lo tanto, el operador GroupJoin permite basar los resultados en el conjunto de coincidencias entero para un elemento externo. Principio de la pgina

Sintaxis de consultas
La sentencia de C# foreach ofrece una sintaxis declarativa para la iteracin sobre los mtodos de las interfaces IEnumerable/IEnumerator de .NET Framework. La sentencia foreach es estrictamente opcional, pero es un mecanismo del lenguaje que ha demostrado ser muy conveniente y popular. Apoyndose en este precedente, la sintaxis de consultas simplifica las expresiones de consulta con una sintaxis declarativa para los operadores de consulta ms comunes: Where, Join, GroupJoin, Select, SelectMany, GroupBy, OrderBy, ThenBy, OrderByDescending y ThenByDescending. Comencemos examinando la sencilla consulta con la que comenzamos este documento:
IEnumerable<string> expr = names .Where(s => s.Length == 5) .OrderBy(s => s) .Select(s => s.ToUpper());

Utilizando la sintaxis de consultas, podemos rescribir esta sentencia de la siguiente forma:


IEnumerable<string> expr = from s in names where s.Length == 5 orderby s select s.ToUpper();

De modo similar a la sentencia foreach de C#, las expresiones de la sintaxis de consultas son ms compactas y fciles de leer, pero completamente opcionales. Toda expresin que puede ser escrita mediante sintaxis de consultas tiene una sintaxis correspondiente (ms verbosa) utilizando la notacin de punto. Comencemos analizando la estructura bsica de una expresin de consulta. Cada expresin de consulta en C# comienza con una clusula from y termina con una clusula select o group. La clusula from inicial puede ir seguida de cero o ms clusulas from, let o where. Adicionalmente, cualquier cantidad de clusulas join puede ir inmediatamente despus de una clusula from. Cada clusula from es un generador que introduce una variable de iteracin sobre una secuencia, cada clusula let da nombre al resultado de una expresin, y cada clusula where es un filtro que excluye elementos del resultado. Cada clusula join combina una nueva fuente de datos con los resultados de un from o join anterior. La clusula final select o group puede ir precedida de una clusula orderby que especifica una ordenacin para el

15 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

resultado:
expresin-de-consulta ::= clusula-from cuerpo-consulta cuerpo-consulta ::= clusula-join* (clusula-from clusula-join* | clusula-let | clusula-where)* clusula-orderby? (clusula-select | clusula-groupby) continuacin-consulta? clusula-from ::= from nombreElemento in exprFuente clusula-join ::= join nombreElemento in exprFuente on exprClave equals exprClave (into nombreElemento)? clusula-let ::= let nombreElemento = exprSeleccin clusula-where ::= where predExpr clusula-orderby ::= orderby (exprClave (ascending | descending)?)* clusula-select ::= select exprSeleccin clusula-groupby ::= group exprSeleccin by exprClave continuacin-consulta ::= into nombreElemento cuerpo-consulta

Por ejemplo, observe las dos siguientes expresiones de consulta:


var query1 = from p in people where p.Age > 20 orderby p.Age descending, p.Name select new { p.Name, Senior = p.Age > 30, p.CanCode }; var query2 = from p in people where p.Age > 20 orderby p.Age descending, p.Name group new { p.Name, Senior = p.Age > 30, p.CanCode } by p.CanCode;

El compilador trata estas expresiones de consulta como si hubieran sido escritas utilizando la siguiente notacin de punto explcita: var query1 = people.Where(p => p.Age > 20) .OrderByDescending(p => p.Age) .ThenBy(p => p.Name) .Select(p => new { p.Name, Senior = p.Age > 30, p.CanCode });
var query2 = people.Where(p => p.Age > 20) .OrderByDescending(p => p.Age) .ThenBy(p => p.Name) .GroupBy(p => p.CanCode, p => new { p.Name, Senior = p.Age > 30, p.CanCode });

Las expresiones de consulta realizan una traduccin mecnica en llamadas a mtodos con nombres especficos. La implementacin exacta de los operadores de consulta que es elegida, por lo tanto, depende tanto del tipo de las variables consultadas como de los mtodos extensores que estn en mbito. Las expresiones de consulta mostradas hasta ahora han utilizado un nico generador. Cuando se utiliza ms de un generador, cada generador subsiguiente es evaluado en el contexto de su predecesor. Por ejemplo, observe esta ligera modificacin de nuestra consulta: var query = from s1 in names where s1.Length == 5 from s2 in names where s1 == s2 select s1 + " " +

16 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

s2; Cuando es ejecutada contra el array de entrada:


string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" };

obtenemos los siguientes resultados:


Burke Burke Frank Frank David David

La expresin de consulta anterior se expande a la siguiente expresin con notacin de punto:


var query = names.Where(s1 => s1.Length == 5) .SelectMany(s1 => names.Where(s2 => s1 == s2) .Select(s2 => s1 + " " + s2) );

Observe que la utilizacin de SelectMany hace que la expresin de consulta interna sea aplanada en el resultado externo. Una clase especial de generador es la clusula join, que introduce los elementos provenientes de otra fuente que coinciden segn las claves indicadas con los elementos de la clusula from o join precedente. Una clusula join puede producir los elementos coincidentes uno a uno, pero si es especificada con una clusula into, los elementos coincidentes se devolvern como un grupo:
var query = from n in names join p in people on n equals p.Name into matching select new { Name = n, Count = matching.Count() };

No es nada sorprendente que esta consulta se expanda de una manera bastante directa en una que ya hemos visto antes:
var query = names.GroupJoin(people, n => n, p => p.Name, (n, matching) => new { Name = n, Count = matching.Count() } );

Frecuentemente es til tratar los resultados de una consulta como generador para una consulta subsiguiente. Para dar soporte a esto, las expresiones de consulta utilizan la palabra clave into para desplegar una nueva expresin de consulta despus de una clusula select o group. La palabra clave into es especialmente til para el post-procesamiento de los resultados de una clusula group by. Por ejemplo, observe este programa:
var query = from item in names orderby item group item by item.Length into lengthGroups orderby lengthGroups.Key descending select lengthGroups; foreach (var group in query) { Console.WriteLine("Strings of length {0}", group.Key); foreach (var val in group.Group) Console.WriteLine(" {0}", val); }

Este programa imprime lo siguiente:


Strings of length 7 Everett Strings of length 6 Albert Connor George Harris Strings of length 5 Burke

17 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

David Frank

Esta seccin ha descrito cmo C# implementa las expresiones de consulta. Otros lenguajes pueden elegir ofrecer sintaxis explcita para operadores de consulta adicionales, o simplemente no ofrecer las expresiones de consulta. Es importante sealar que la sintaxis de consultas no est cableada de ninguna manera a los operadores de consulta estndar. Es una caracterstica puramente sintctica que puede aplicarse a cualquier cosa que satisfaga el patrn LINQ implementando los mtodos subyacentes con los nombres y firmas apropiados. Los operadores de consulta estndar descritos anteriormente hacen esto utilizando mtodos extensores para aumentar la interfaz IEnumerable<T>. Los desarrolladores pueden utilizar la sintaxis de consultas sobre cualquier tipo que deseen, siempre que se aseguren de que ste se adhiere al patrn LINQ, ya sea mediante implementacin directa de los mtodos necesarios o aadindolos como mtodos extensores. Esta extensibilidad es explotada en el propio Proyecto LINQ a travs de la provisin de dos API basadas en LINQ: DLinq, que implementa el patrn LINQ para el acceso a datos basados en SQL, y XLinq, que permite las consultas LINQ sobre datos XML. Ambas extensiones se describen en las siguientes secciones. Principio de la pgina

DLinq: Integracin de SQL


Las consultas integradas en los lenguajes .NET pueden ser utilizadas para consultar almacenes de datos relacionales sin abandonar la sintaxis o el entorno de tiempo de compilacin del lenguaje de programacin local. Esta facilidad, llamada DLinq, se aprovecha de la integracin de la informacin de esquemas SQL en los metadatos del CLR. Esta integracin compila las definiciones de tablas y vistas SQL dentro de tipos CLR que pueden ser accedidas desde cualquier lenguaje. DLinq define dos atributos principales, [Table] y [Column], que indican qu tipos y propiedades del CLR corresponden a datos SQL externos. El atributo [Table] puede ser aplicado a una clase y asocia el tipo del CLR con una tabla o vista nombrada de SQL. El atributo [Column] puede ser aplicado a cualquier campo o propiedad y asocia el miembro con una columna nombrada de SQL. Ambos atributos tienen parmetros, para permitir el almacenamiento de metadatos especficos de SQL. Por ejemplo, observe esta sencilla definicin de esquema SQL:
create table People ( Name nvarchar(32) primary key not null, Age int not null, CanCode bit not null ) create table Orders ( OrderID nvarchar(32) primary key not null, Customer nvarchar(32) not null, Amount int )

Su equivalente CLR tendra la siguiente apariencia:


[Table(Name="People")] public class Person { [Column(DbType="nvarchar(32) not null", Id=true)] public string Name; [Column] public int Age; [Column] public bool CanCode; } [Table(Name="Orders")] public class Order { [Column(DbType="nvarchar(32) not null", Id=true)] public string OrderID;

18 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

[Column(DbType="nvarchar(32) not null")] public string Customer; [Column] public int? Amount; }

Observe de este ejemplo que las columnas que permiten valores nulos se mapean a tipos anulables del CLR (los tipos anulables aparecieron en la versin 2 de .NET Framework), y que para los tipos de SQL que no tienen una correspondencia 1:1 con un tipo CLR (por ejemplo, nvarchar, char, text), el tipo SQL original es memorizado en los metadatos del CLR. Para ejecutar una consulta contra un almacn relacional, la implementacin de DLinq del patrn LINQ traduce la consulta de su rbol de expresin a una expresin SQL y un objeto DbCommand de ADO.NET adecuado para la evaluacin remota. Por ejemplo, observe esta simple consulta:
// establecer contexto de consulta sobre una conexin de ADO.NET DataContext context = new DataContext( "Initial Catalog=petdb;Integrated Security=sspi"); // obtener variables que representan las tablas remotas // que corresponden a los tipos Person y Order del CLR Table<Person> custs = context.GetTable<Person>(); Table<Order> orders = context.GetTable<Order>(); // construir la consulta var query = from c in custs from o in orders where o.Customer == c.Name select new { c.Name, o.OrderID, o.Amount, c.Age }; // ejecutar la consulta foreach (var item in query) Console.WriteLine("{0} {1} {2} {3}", item.Name, item.OrderID, item.Amount, item.Age);

El tipo DataContext ofrece un traductor ligero que se encarga de traducir los operadores de consulta estndar a SQL. DataContext utiliza un objeto de ADO.NET IDbConnection existente para acceder al almacn, y puede ser inicializado bien mediante un objeto que represente a una conexin ya establecida o a una cadena de conexin que puede ser utilizada para crear una. El mtodo GetTable ofrece variables compatibles con IEnumerable que pueden ser utilizadas en expresiones de consulta para representar la tabla o vista remota. Las llamadas a GetTable no causan ninguna interaccin con la base de datos en vez de eso, representan el potencial de interactuar con la tabla o vista remota utilizando expresiones de consulta. En nuestro ejemplo anterior, la consulta no es transmitida al almacn de datos hasta que el programa itera sobre la expresin de consulta, en este caso utilizando la sentencia foreach de C#. Cuando el programa itera por primera vez sobre la consulta, la maquinaria del DataContext traduce el rbol de expresin en la siguiente sentencia SQL que es enviada al almacn:
SELECT [t0].[Age], [t1].[Amount], [t0].[Name], [t1].[OrderID] FROM [Customers] AS [t0], [Orders] AS [t1] WHERE [t1].[Customer] = [t0].[Name]

Es importante destacar que incorporando directamente la posibilidad de consultas en el lenguaje de programacin local, los desarrolladores obtienen toda la potencia del modelo relacional sin tener que cocer estticamente las relaciones dentro del tipo del CLR. Dicho esto, un mapeado objeto/relacional completo podra tambin aprovechar esta capacidad bsica de consulta para aquellos usuarios que deseen tal funcionalidad. DLinq ofrece una funcionalidad de mapeado objeto/relacional con la que el desarrollador puede definir y navegar por las relaciones entre objetos. Usted puede referirse a Orders (Pedidos) como una propiedad de la clase Customer (Cliente) utilizando un mapeado, de modo que no se necesite un encuentro explcito para combinar ambas. Los ficheros de mapeado externos permiten separar el mapeado del modelo de objetos para obtener unas posibilidades ms amplias de mapeado.

19 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Principio de la pgina

XLinq: Integracin de XML


Las consultas integradas en los lenguajes .NET para XML (XLinq) permiten que los datos XML sean consultados utilizando los operadores de consulta estndar, as como mediante operadores especficos de rboles que ofrecen posibilidades de navegacin por los descendientes, ancestros y hermanos al estilo de XPath. Ofrecen una representacin eficiente en memoria para XML que se integra con la infraestructura de lectura/escritura de System.Xml existente y es ms fcil de uasr que el DOM de W3C. Tres tipos se encargan de la mayor parte del trabajo de integracin de XML con las consultas: XName, XElement y XAttribute. XName suministra una manera sencilla de trabajar con los identificadores cualificados mediante espacios de nombres (QNames) utilizados tanto en los nombres de elementos como de atributos. XName gestiona de manera transparente la atomizacin eficiente de identificadores y permite que se utilicen smbolos o simples cadenas dondequiera que se necesite un QName. Los elementos y atributos XML se representan mediante XElement y XAttribute, respectivamente. XElement y XAttribute soportan la sintaxis normal de construccin, permitiendo a los desarrolladores escribir expresiones XML usando una sintaxis natural:
var e = new XElement("Person", new XAttribute("CanCode", true), new XElement("Name", "Loren David"), new XElement("Age", 31)); var s = e.ToString();

Esto corresponde al siguiente XML:


<Person CanCode="true"> <Name>Loren David</Name> <Age>31</Age> </Person>

Observe que no es necesario ningn patrn fbrica basado en DOM para crear la expresin XML, y que la implementacin de ToString produce el XML textual. Los elementos XML tambin pueden ser construidos a partir de un XmlReader o de un literal de cadena:
var e2 = XElement.Load(xmlReader); var e1 = XElement.Parse( @"<Person CanCode='true'> <Name>Loren David</Name> <Age>31</Age> </Person>"); XElement tambin soporta la emisin de XML utilizando el tipo existente XmlWriter.

XElement conecta con los operadores de consulta, permitiendo a los desarrolladores escribir consultas contra informacin no basada en XML y producir resultados XML construyendo objetos XElement en el cuerpo de la clusula select:
var query = from p in people where p.CanCode select new XElement("Person", new XAttribute("Age", p.Age), p.Name);

Esta consulta devuelve una secuencia de XElement. Para permitir que los objetos XElement se construyan a partir del resultado de esta clase de consultas, el constructor de XElement permite que se le pase directamente como argumento una secuencia de elementos:
var x = new XElement("People", from p in people where p.CanCode select new XElement("Person", new XAttribute("Age", p.Age), p.Name));

20 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Esta expresin XML resulta en el siguiente XML:


<People> <Person Age="11">Allen Frances</Person> <Person Age="59">Connor Morgan</Person> </People>

La sentencia anterior tiene una traduccin directa a Visual Basic. Sin embargo, Visual Basic 9.0 tambin soporta el uso de literales XML, que permiten que las expresiones de consulta sean expresadas utilizando una sintaxis XML declarativa directamente dentro de Visual Basic. El ejemplo anterior pudo haberse construido con la sentencia de Visual Basic:
Dim x = _ <People> <%= From p In people __ Select <Person Age=<%= p.Age %>>p.Name</Person> _ Where p.CanCode _ %> </People>

Los ejemplos hasta el momento han mostrado cmo construir nuevos valores XML utilizando consultas integradas en los lenguajes. Los tipos XElement y XAttribute tambin simplifican la extraccin de informacin de estructuras XML. XElement ofrece mtodos de acceso que permiten aplicar expresiones de consulta a los ejes tradicionales de XPath. Por ejemplo, la siguiente consulta extrae nicamente los nombres del XElement mostrado antes:
IEnumerable<string> justNames = from e in x.Descendants("Person") select e.Value; //justNames = ["Allen Frances", "Connor Morgan"]

Para extraer valores estructurados del XML, simplemente utilizamos una expresin de inicializador en nuestra clusula select:
IEnumerable<Person> persons = from e in x.Descendants("Person") select new Person { Name = e.Value, Age = (int)e.Attribute("Age") };

Observe que tanto XAttribute como XElement soportan conversiones explcitas para extraer el valor textual como un tipo primitivo. Para gestionar los datos ausentes, podemos simplemente convertir a un tipo anulable:
IEnumerable<Person> persons = from e in x.Descendants("Person") select new Person { Name = e.Value, Age = (int?)e.Attribute("Age") ?? 21 };

En este caso, se utiliza un valor predeterminado de 21 cuando el atributo Age est ausente. Visual Basic 9.0 ofrece soporte directo en el lenguaje para los mtodos de acceso Elements, Attribute y Descendants de XElement, permitiendo que los datos basados en XML sean accedidos utilizando una sintaxis ms compacta y directa llamada propiedades de los ejes XML. Podemos utilizar esta funcionalidad para escribir la sentencia C# anterior de la siguiente forma:
Dim persons = _ From e In x...<Person> _ Select new Person { _ .Name = e.Value, _ .Age = e.@Age.Value ?? 21 _ }

En Visual Basic, x...<Person> recupera todos los elementos en la coleccin Descendants de x que tienen el nombre Person, mientras que la expresin e.@Age encuentra todos los XAttributes con el nombre Age. La propiedad Value obtiene el primer atributo de la coleccin y llama a la propiedad Value de ese atributo.

21 de 22

26/03/2012 12:47 p.m.

El proyecto LINQ

http://msdn.microsoft.com/es-es/library/bb308959(d=printer).aspx

Principio de la pgina

Conclusin
Las consultas integradas en los lenguajes .NET aaden capacidades de consulta al CLR y a los lenguajes orientados a l. La facilidad de consulta se apoya en las expresiones lambda y los rboles de expresiones para permitir que los predicados, proyecciones y expresiones de extraccin de claves puedan utilizarse como cdigo ejecutable opaco o como datos transparentes en memoria adecuados para su tratamiento posterior o traduccin. Los operadores de consulta estndar definidos por el Proyecto LINQ operan sobre cualquier fuente de informacin basada en IEnumerable<T>, y se integran con ADO.NET (DLinq) y System.Xml (XLinq) para permitir que los datos relacionales y XML obtengan los beneficios de las consultas integradas en los lenguajes. Principio de la pgina

Contenido de la comunidad
2012 Microsoft. Reservados todos los derechos.

22 de 22

26/03/2012 12:47 p.m.

Вам также может понравиться