Anda di halaman 1dari 16

LINQ (Language-Integrated Query)

LINQ

Language-Integrated Query (LINQ) es un conjunto de características


presentado en Visual Studio 2008 que agrega capacidades de consulta eficaces
a la sintaxis de los lenguajes C# y Visual Basic. LINQ incluye patrones
estándar y de fácil aprendizaje para consultar y actualizar datos, y su
tecnología se puede extender para utilizar potencialmente cualquier tipo de
almacén de datos. Visual Studio incluye ensamblados de proveedores para
LINQ que habilitan el uso de LINQ con colecciones de .NET Framework, bases
de datos SQL Server, conjuntos de datos de ADO.NET y documentos XML.

Introducción a LINQ

Language-Integrated Query (LINQ) es una innovación introducida en Visual


Studio 2008 y .NET Framework versión 3.5 que elimina la distancia que separa
el mundo de los objetos y el mundo de los datos.

Tradicionalmente, las consultas con datos se expresan como cadenas sencillas,


sin comprobación de tipos en tiempo de compilación ni compatibilidad con
IntelliSense. Además, es necesario aprender un lenguaje de consultas
diferente para cada tipo de origen de datos: bases de datos SQL, documentos
XML, servicios Web diversos, etc. LINQ convierte una consulta en una
construcción de lenguaje de primera clase en C# y Visual Basic. Las consultas
se escriben para colecciones de objetos fuertemente tipadas, utilizando
palabras clave del lenguaje y operadores con los que se está familiarizado. La
ilustración siguiente muestra una consulta LINQ parcialmente completada en
una base de datos SQL Server en C#, con comprobación de tipos completa y
compatibilidad con IntelliSense.
En Visual Studio se pueden escribir consultas LINQ en Visual Basic o en C# con
bases de datos SQL Server, documentos XML, conjuntos de datos ADO.NET y
cualquier colección de objetos que admita IEnumerable o la interfaz genérica
IEnumerable(Of T). También se ha previsto la compatibilidad de LINQ con
ADO.NET Entity Framework, y otros fabricantes se encuentran escribiendo
proveedores LINQ para muchos servicios Web y otras implementaciones de
bases de datos.

Puede utilizar consultas LINQ en proyectos nuevos o junto a consultas que no


son LINQ en proyectos existentes. El único requisito es que el proyecto esté
orientado a .NET Framework 3.5 o posterior.

Introducción a las consultas LINQ (C#)

Una consulta es una expresión que recupera datos de un origen de datos. Las
consultas normalmente se expresan en un lenguaje de consultas especializado.
A lo largo del tiempo se han ido desarrollando lenguajes diferentes para los
distintos tipos de orígenes de datos, como SQL para las bases de datos
relacionales y XQuery para XML. Por tanto, los desarrolladores han tenido que
aprender un nuevo lenguaje de consulta para cada tipo de origen de datos o
formato de datos que deben usar. LINQ simplifica esta situación al
proporcionar un modelo coherente para trabajar con los datos de varios tipos
de formatos y orígenes de datos. En una consulta LINQ, siempre se trabaja con
objetos. Se utilizan los mismos modelos de codificación básicos para consultar
y transformar datos de documentos XML, bases de datos SQL, conjuntos de
datos ADO.NET, colecciones .NET y cualquier otro formato para el que haya
disponible un proveedor LINQ.
Las tres partes de una operación de consulta

Todas las operaciones de consulta LINQ se componen de tres acciones


distintas:

1. Obtención del origen de datos.


2. Creación de la consulta.
3. Ejecución de la consulta.

En el ejemplo siguiente se muestra cómo se expresan las tres partes de una


operación de consulta en el código fuente. En el ejemplo se utiliza por
comodidad una matriz de enteros como origen de datos, pero los mismos
conceptos se aplican a otros orígenes de datos. En el resto del tema se hace
referencia a este ejemplo.

class IntroToLINQ
{
static void Main()
{
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;

// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}

En la siguiente ilustración se muestra la operación de consulta completa. En


LINQ, la ejecución de la consulta es distinta de la propia consulta; en otras
palabras, no se recuperan datos con la simple creación de la variable de
consulta.
El origen de datos

En el ejemplo anterior, como el origen de datos es una matriz, se admite


implícitamente la interfaz genérica IEnumerable<T>. Este hecho implica que
se puede consultar con LINQ. Una consulta se ejecuta en una instrucción
foreach y foreach requiere IEnumerable o IEnumerable<T>. Tipos que admiten
una interfaz derivada o IEnumerable<T> como el IQueryable<T> genérico se
denominan tipos consultable.

Un tipo que se puede consultar no requiere ninguna modificación o tratamiento


especial para servir como origen de datos LINQ. Si los datos de origen aún no
están en memoria como tipo que se puede consultar, el proveedor LINQ debe
representarlos como tales. Por ejemplo, LINQ to XML carga un documento XML
en un tipo XElement que se puede consultar:

// Create a data source from an XML document.


// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Con LINQ to SQL, primero se crea una asignación relacional de objetos en


tiempo de diseño, ya sea manualmente o mediante el Object Relational
Designer. Después, se escriben las consultas en los objetos y, en tiempo de
ejecución, LINQ to SQL controla la comunicación con la base de datos. En el
siguiente ejemplo, Customers representa una tabla concreta en la base de
datos y el tipo del resultado de la consulta, IQueryable<T>, deriva de
IEnumerable<T>.

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.


IQueryable<Customer> custQuery =
from cust in db.Customers
where cust.City == "London"
select cust;

Para obtener más información sobre cómo crear tipos de orígenes de datos
específicos, consulte la documentación de los distintos proveedores LINQ. Sin
embargo, la regla básica es muy simple: un origen de datos LINQ es cualquier
objeto que admite la interfaz genérica IEnumerable<T> o una interfaz que
herede de ella.

Nota

También se pueden utilizar tipos como ArrayList, que admite la interfaz no


genérica IEnumerable, como origen de datos LINQ. Para obtener más
información, vea Cómo: Consultar un objeto ArrayList con LINQ.
La consulta

La consulta especifica qué información se va a recuperar de uno o varios


orígenes de datos. Opcionalmente, una consulta también especifica cómo
debería ordenarse, agruparse y darse forma a esa información antes de ser
devuelta. Una consulta se almacena en una variable de consulta y se inicializa
con una expresión de consulta. Para simplificar la escritura de consultas, C#
incluye nueva sintaxis de consulta.

La consulta del ejemplo anterior devuelve todos los números pares de la matriz
de enteros. La expresión de consulta contiene tres cláusulas: from, where y
select. (Si está familiarizado con SQL, habrá observado que el orden de las
cláusulas se invierte respecto al orden de SQL.) La cláusula from especifica el
origen de datos, la cláusula where aplica el filtro y la cláusula select especifica
el tipo de los elementos devueltos. Ésta y las demás cláusulas de consulta se
analizan con detalle en la sección Expresiones de consultas LINQ (Guía de
programación de C#). Aquí, lo importante es que, en LINQ, la propia variable
de consulta no realiza ninguna acción ni devuelve datos. Simplemente
almacena la información necesaria para generar los resultados cuando la
consulta se ejecute posteriormente. Para obtener más información sobre cómo
se construyen las consultas en segundo plano, vea Información general sobre
operadores de consulta estándar.

Nota

Las consultas también se pueden expresar con sintaxis de método. Para


obtener más información, vea Sintaxis de consultas LINQ y sintaxis de
métodos (C#).
Ejecución de la consulta

Ejecución diferida

Como se ha mencionado previamente, la variable de consulta sólo almacena


los comandos de la consulta. La ejecución real de la consulta se aplaza hasta
que se procese una iteración en la variable de consulta, en una instrucción
foreach. Este concepto se conoce como ejecución diferida y se muestra en el
ejemplo siguiente:

// Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}

La instrucción foreach es también donde se recuperan los resultados de la


consulta. Por ejemplo, en la consulta anterior, la variable de iteración num
contiene cada valor (de uno en uno) en la secuencia devuelta.

Dado que la propia variable de consulta nunca contiene los resultados de la


consulta, se puede ejecutar tantas veces como se desee. Por ejemplo, puede
que una aplicación independiente actualice continuamente una base de datos.
En su aplicación, podría crear una consulta que recuperase los datos más
recientes, y podría ejecutarla repetidamente cada cierto tiempo para recuperar
cada vez resultados diferentes.

Forzar la ejecución inmediata

Las consultas que realizan funciones de agregación en un intervalo de


elementos de origen primero deben recorrer en iteración dichos elementos.
Algunos ejemplos de esas consultas son Count, Max, Average y First. Se
ejecutan sin una instrucción foreach explícita porque la propia consulta debe
utilizar foreach para devolver un resultado. Debe saber también que estos
tipos de consultas devuelven un solo valor, no una colección IEnumerable. La
consulta siguiente devuelve un recuento de los números pares de la matriz de
origen:

var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;

int evenNumCount = evenNumQuery.Count();

Para forzar la ejecución inmediata de cualquier consulta y almacenar en


memoria caché sus resultados, puede llamar al método ToList<TSource> o
ToArray<TSource>.

List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();

También puede forzar la ejecución si coloca el bucle foreach justo después de


la expresión de consulta. Sin embargo, al llamar a ToList o ToArray también
se almacenan en memoria caché todos los datos en un objeto de colección
único.

LINQ y tipos genéricos (C#)

Las consultas LINQ se basan en tipos genéricos, que se incluyeron por primera
vez en la versión 2.0 de .NET Framework. No se requieren conocimientos
avanzados de los tipos genéricos para poder empezar a escribir consultas. Sin
embargo, quizás necesite conocer dos conceptos básicos:

1. Al crear una instancia de una clase de colección genérica, como List<T>,


la "T" se reemplaza con el tipo de objetos que contendrá la lista. Por
ejemplo, una lista de cadenas se expresa como List<string> y una lista
de objetos Customer se expresa como List<Customer>. Una lista
genérica tiene establecimiento inflexible de tipos y proporciona muchas
ventajas frente a las colecciones que almacenan sus elementos como
Object. Si intenta agregar Customer a List<string>, obtendrá un error
en tiempo de compilación. Es fácil utilizar las colecciones genéricas,
porque no es necesario realizar conversiones de tipos en tiempo de
ejecución.
2. IEnumerable<T> es la interfaz que permite enumerar las clases de
colección genéricas mediante la instrucción foreach. Las clases de
colección genéricas admiten IEnumerable<T> de la misma forma que
las clases de colección no genéricas, como ArrayList, admiten
IEnumerable.

Para obtener más información acerca de los tipos genéricos, vea Genéricos
(Guía de programación de C#).

Variables IEnumerable<T> en consultas LINQ

Las variables de consulta LINQ tienen tipos IEnumerable<T> o un tipo


derivado, como IQueryable<T>. Cuando se encuentre una variable de consulta
de tipo IEnumerable<Customer>, sólo significa que la consulta, cuando se
ejecute, generará una secuencia de cero o más objetos Customer.

IEnumerable<Customer> customerQuery =
from cust in customers
where cust.City == "London"
select cust;

foreach (Customer customer in customerQuery)


{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}

Para obtener más información, vea Relaciones entre tipos en operaciones de


consulta LINQ (C#).

Permitir al compilador administrar las declaraciones de tipos genéricos


Si lo prefiere, puede evitar la sintaxis genérica con el uso de la palabra clave
var. La palabra clave var indica al compilador que deduzca el tipo de una
variable de consulta examinando el origen de datos especificado en la cláusula
from. En el ejemplo siguiente se genera el mismo código compilado que en el
ejemplo anterior:

var customerQuery2 =
from cust in customers
where cust.City == "London"
select cust;

foreach(var customer in customerQuery2)


{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}

La palabra clave var es útil cuando el tipo de la variable es obvio o cuando no


es tan importante especificar explícitamente los tipos genéricos anidados,
como los que se generan en las consultas de grupo. Por lo general, si utiliza
var, debe saber que puede dificultar la legibilidad del código para los demás.
Para obtener más información, vea Variables locales con asignación implícita
de tipos (Guía de programación de C#).

Operaciones básicas de consulta LINQ (C#)

En este tema se ofrece una breve introducción a las expresiones de consulta


LINQ y algunas de las clases de operaciones típicas que se realizan en una
consulta. En los temas siguientes se ofrece información más detallada:

Expresiones de consultas LINQ (Guía de programación de C#)

Información general sobre operadores de consulta estándar

Nota
Si ya está familiarizado con un lenguaje de consultas como SQL o XQuery,
puede omitir la mayoría de este tema. Lea la parte dedicada a la cláusula from
en la sección siguiente para obtener información sobre el orden de las
cláusulas en las expresiones de consulta LINQ.
Obtener un origen de datos
En una consulta LINQ, el primer paso es especificar el origen de datos. En C#,
como en la mayoría de los lenguajes de programación, se debe declarar una
variable antes de poder utilizarla. En una consulta LINQ, la cláusula from
aparece en primer lugar para introducir el origen de datos (customers) y la
variable de rango (cust).

//queryAllCustomers is an IEnumerable<Customer>
var queryAllCustomers = from cust in customers
select cust;

La variable de rango es como la variable de iteración en un bucle foreach, con


la diferencia de que en una expresión de consulta realmente no se produce
ninguna iteración. Cuando se ejecuta la consulta, la variable de rango actúa
como referencia para cada elemento sucesivo de customers. Dado que el
compilador puede deducir el tipo de cust, no tiene que especificarlo
explícitamente. Una cláusula let puede introducir variables de rango
adicionales. Para obtener más información, vea let (Cláusula, Referencia de
C#).

Nota
Para los orígenes de datos no genéricos, como ArrayList, el tipo de la variable
de rango debe establecerse explícitamente. Para obtener más información, vea
Cómo: Consultar un objeto ArrayList con LINQ y from (Cláusula, Referencia de
C#).
Filtrar

Probablemente la operación de consulta más común es aplicar un filtro en


forma de expresión booleana. El filtro hace que la consulta devuelva sólo los
elementos para los que la expresión es verdadera. El resultado se genera
mediante la cláusula where. El filtro aplicado especifica qué elementos se
deben excluir de la secuencia de origen. En el ejemplo siguiente, sólo se
devuelven los customers cuya dirección se encuentra en Londres (London).

var queryLondonCustomers = from cust in customers


where cust.City == "London"
select cust;

Puede utilizar los operadores lógicos AND y OR de C#, con los que ya estará
familiarizado, para aplicar las expresiones de filtro que sean necesarias en la
cláusula where. Por ejemplo, para devolver sólo los clientes con dirección en
"London" AND cuyo nombre sea "Devon", escribiría el código siguiente:

where cust.City=="London" && cust.Name == "Devon"

Para devolver los clientes con dirección en Londres o París, escribiría el código
siguiente:

where cust.City == "London" || cust.City == "Paris"

Para obtener más información, vea where (Cláusula, Referencia de C#).

Ordering

A menudo es necesario ordenar los datos devueltos. La cláusula orderby hará


que los elementos de la secuencia devuelta se ordenen según el comparador
predeterminado del tipo que se va a ordenar. Por ejemplo, la consulta
siguiente se puede extender para ordenar los resultados según la propiedad
Name. Dado que Name es una cadena, el comparador predeterminado realiza
una ordenación alfabética de la A a la Z.

var queryLondonCustomers3 =
from cust in customers
where cust.City == "London"
orderby cust.Name ascending
select cust;

Para ordenar los resultados en orden inverso, de la Z a la A, utilice la cláusula


orderby…descending.

Para obtener más información, vea orderby (Cláusula, Referencia de C#).

Grupo

La cláusula group permite agrupar los resultados según la clave que se


especifique. Por ejemplo, podría especificar que los resultados se agrupen por
City para que todos los clientes de London o París estén en grupos
individuales. En este caso, la clave es cust.City.

Nota
En los ejemplos siguientes, los tipos son explícitos, para ilustrar el concepto.
También podría utilizar tipos implícitos para custQuery, group y customer,
para que el compilador determine el tipo exacto.

// queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>


var queryCustomersByCity =
from cust in customers
group cust by cust.City;

// customerGroup is an IGrouping<string, Customer>


foreach (var customerGroup in queryCustomersByCity)
{
Console.WriteLine(customerGroup.Key);
foreach (Customer customer in customerGroup)
{
Console.WriteLine(" {0}", customer.Name);
}
}

Al finalizar una consulta con una cláusula group, los resultados adoptan la
forma de una lista de listas. Cada elemento de la lista es un objeto que tiene
un miembro Key y una lista de elementos agrupados bajo esa clave. Al
procesar una iteración en una consulta que genera una secuencia de grupos,
debe utilizar un bucle foreach anidado. El bucle exterior recorre en iteración
cada grupo y el bucle interior recorre en iteración los miembros de cada grupo.

Si debe hacer referencia a los resultados de una operación de grupo, puede


utilizar la palabra clave into para crear un identificador con el que se puedan
realizar más consultas. La consulta siguiente devuelve sólo los grupos que
contienen más de dos clientes:

// custQuery is an IEnumerable<IGrouping<string, Customer>>


var custQuery =
from cust in customers
group cust by cust.City into custGroup
where custGroup.Count() > 2
orderby custGroup.Key
select custGroup;
Para obtener más información, vea group (Cláusula, Referencia de C#).

Combinación

Las operaciones de combinación crean asociaciones entre las secuencias que


no se modelan explícitamente en los orígenes de datos. Por ejemplo puede
realizar una unión para encontrar todos los clientes y distribuidores que tienen
la misma ubicación. En LINQ, la cláusula join funciona siempre con colecciones
de objetos, en lugar de con tablas de base de datos directamente.

var innerJoinQuery =
from cust in customers
join dist in distributors on cust.City equals dist.City
select new { CustomerName = cust.Name, DistributorName = dist.Name };

En LINQ no es necesario utilizar join tan a menudo como en SQL, porque las
claves externas en LINQ se representan en el modelo de objetos como
propiedades que contienen una colección de elementos. Por ejemplo, un objeto
Customer contiene una colección de objetos Order. En lugar de realizar una
combinación, se tiene acceso a los pedidos utilizando la notación de punto:

from order in Customer.Orders...

Para obtener más información, vea join (Cláusula, Referencia de C#).

Selección (proyecciones)

La cláusula select genera resultados de consulta y especifica la "forma" o el


tipo de cada elemento devuelto. Por ejemplo, puede especificar si sus
resultados estarán compuestos de objetos Customer completos, un solo
miembro, un subconjunto de miembros o algún tipo de resultado
completamente diferente basado en un cálculo o en un objeto nuevo. Cuando
la cláusula select genera algo distinto de una copia del elemento de origen, la
operación se denomina proyección. El uso de proyecciones para transformar
los datos es una eficaz funcionalidad de las expresiones de consulta LINQ.

Relaciones entre tipos en operaciones de consulta LINQ (C#)

Para escribir las consultas eficazmente, es necesario comprender cómo se


relacionan entre sí los tipos de las variables en una operación de consulta
completa. Si entiende estas relaciones comprenderá más fácilmente los
ejemplos de LINQ y los ejemplos de código de la documentación. Además,
entenderá lo que sucede en segundo plano cuando los tipos de las variables se
declaran implícitamente mediante var.

Las operaciones de consulta LINQ tienen establecimiento inflexible de tipos en


el origen de datos, en la propia consulta y en la ejecución de la consulta. El
tipo de las variables de la consulta debe ser compatible con el tipo de los
elementos del origen de datos y con el tipo de la variable de iteración de la
instrucción foreach. Este establecimiento inflexible de tipos garantiza que los
errores de tipos se detectan en tiempo de compilación, cuando aún se pueden
corregir, antes de que los usuarios los detecten.

Para mostrar estas relaciones de tipos, en la mayoría de los ejemplos


siguientes se utilizan tipos explícitos para todas las variables. En el último
ejemplo se muestra cómo se aplican los mismos principios incluso al utilizar
tipos implícitos mediante var.

Consultas que no transforman los datos de origen

La ilustración siguiente muestra una operación de consulta LINQ to Objects


que no realiza ninguna transformación de los datos. El origen contiene una
secuencia de cadenas y el resultado de la consulta también es una secuencia
de cadenas.

1. El argumento de tipo del origen de datos determina el tipo de la variable


de rango.
2. El tipo del objeto que está seleccionado determina el tipo de la variable
de consulta. Aquí, name es una cadena. Por lo tanto, la variable de
consulta es IEnumerable<string>.
3. La variable de consulta se procesa en iteración en la instrucción foreach.
Dado que la variable de consulta es una secuencia de cadenas, la
variable de iteración también es una cadena.
Consultas que transforman los datos de origen

La ilustración siguiente muestra una operación de consulta LINQ to SQL que


realiza una transformación simple de los datos. La consulta utiliza una
secuencia de objetos Customer como entrada y selecciona sólo la propiedad
Name en el resultado. Dado que Name es una cadena, la consulta genera una
secuencia de cadenas como resultado.

1. El argumento de tipo del origen de datos determina el tipo de la variable


de rango.
2. La instrucción select devuelve la propiedad Name en lugar del objeto
Customer completo. Dado que Name es una cadena, el argumento de
tipo de custNameQuery es string, no Customer.
3. Dado que custNameQuery es una secuencia de cadenas, la variable de
iteración del bucle foreach también debe ser string.

La ilustración siguiente muestra una transformación un poco más compleja. La


instrucción select devuelve un tipo anónimo que captura sólo dos miembros del
objeto Customer original.
1. El argumento de tipo del origen de datos siempre es el tipo de la
variable de rango de la consulta.
2. Dado que la instrucción select genera un tipo anónimo, el tipo de la
variable de consulta debe declararse implícitamente mediante var.
3. Dado que el tipo de la variable de consulta es implícito, la variable de
iteración del bucle foreach también debe ser implícita.

Permitir que el compilador deduzca la información de tipo

Aunque debería comprender las relaciones de los tipos en una operación de


consulta, tiene la opción de que el compilador haga todo el trabajo por usted.
La palabra clave var se puede utilizar para cualquier variable local en una
operación de consulta. La ilustración siguiente es totalmente equivalente al
ejemplo número 2 antes analizado. La única diferencia es que el compilador
proporcionará el tipo seguro de cada variable en la operación de la consulta:

Anda mungkin juga menyukai