Scroll to top

Spanish (Español) translation by Steven (you can also view the original English article)

Como desarrolladores de software, dedicamos mucho tiempo a extraer y mostrar datos de diferentes fuentes de datos. Ya sea un servicio web XML de algún tipo o una base de datos relacional con todas las funciones, nos hemos visto obligados a aprender diferentes métodos de acceso a datos. ¿No sería fantástico si el método de acceso fuera el mismo para todas las fuentes de datos? Bueno, estamos de suerte porque, a partir del lanzamiento de C# 3.0 y el Framework .NET 3.5, LINQ ha venido a cambiar el juego para siempre.

Detalles del tutorial

  • Introducción a la sintaxis de LINQ
  • Proyecciones usando LINQ
  • Refinando datos
  • Operadores estándar

Visión general del acceso a los datos actuales

En la plataforma .NET hemos estado y todavía estamos utilizando ADO.NET 
para acceder a diferentes fuentes de datos. La comunidad de código abierto también ha proporcionado 
los desarrolladores con una serie de alternativas.

Language Integrated Query es la nueva adición a la familia .NET 
y como su nombre indica es el tipo de acceso a datos de estilo de consulta que 
es totalmente compatible con el idioma para unificar de manera efectiva la forma en que accedemos a los datos 
y para hacer nuestras vidas más fáciles. LINQ puede apuntar a una serie de fuentes diferentes, como Oracle, 
MSSQL, XML y algunos otros, pero por ahora nos centraremos en lo más básico de 
todos, LINQ a Objects.

LINQ a Objects.

Normalmente, para procesar y refinar los datos dentro de nuestras listas 
y varias otras estructuras de datos, hemos utilizado el bucle 'foreach' u otro 
tipo de método de bucle para iterar a través de los objetos y procesarlos uno por 
uno de acuerdo a alguna condición. Esto está bien, pero, francamente, requiere una gran cantidad de
codificación básica que todos deseamos no tener que escribir. Esencialmente hemos tenido que decirle al 
compilador todos los detalles del proceso para manipular los datos.

Aquí es exactamente donde LINQ brilla mejor. Lo que LINQ nos permite 
hacer es simplemente decirle al compilador lo que nos gustaría realizar y dejar que el compilador funcione 
de la mejor manera para lograrlo. Si has usado la sintaxis SQL anteriormente, las semejanzas masivas 
entre LINQ y cualquier dialecto de SQL será lo primero que notarás.
Al igual que SQL, LINQ también soporta "select", "from", "where", "join", "group by"
y "order by". Aquí hay un ejemplo simple de consultar una lista de objetos:

Inicialización de la lista:

1
2
List<Car> ListOfCars = new List<Car>()
3
{
4
    new Car {name = "Toyota"    , owner = "Alex" , model = 1992},
5
    new Car {name = "Mitsubishi", owner = "Jeff" , model = 2005},
6
    new Car {name = "Land Rover", owner = "Danny", model = 2001},
7
    new Car {name = "BMW"       , owner = "Danny", model = 2006},
8
    new Car {name = "Subaru"    , owner = "Smith", model = 2003}
9
};

La consulta:

1
2
IEnumerable<Car> QueryResult = from car in ListOfCars
3
                               select car;

La primera parte del código anterior simplemente rellena una lista 
Con cuatro instancias de la clase 'Car'. La siguiente parte del código, sin embargo, utiliza la 
"from" y "select" para seleccionar un grupo de objetos. La principal diferencia 
entre SQL y LINQ es que la palabra clave "from" aparece antes de la palabra clave "select" 
porque primero debemos definir el objeto que queremos operar. Finalmente 
la cláusula "select" le dice al compilador lo que deseamos extraer en esta consulta. El anterior 
código simplemente extrae todo lo que está en la lista y lo asigna a la variable 
"QueryResult".

Cuando consultamos cosas desde objetos (LINQ to Objects), nuestras 
consultas siempre devuelven una lista de objetos "IEnumerable<T>". Esencialmente el 
tipo "IEnumerable" es el tipo de lista que expone el enumerador, que 
admite una iteración simple sobre una colección no genérica, y <T>
es el tipo de cada entrada en la lista.

No te preocupes si no estás familiarizado con los "enumeradores" y los "genéricos". Sólo 
recuerda que el resultado de las consultas LINQ es siempre una colección como datos 
estructurados que permiten iterar a través de ellos utilizando un bucle como se muestra 
abajo:

1
2
foreach(Car car in QueryResult)
3
    Console.WriteLine(car.name);

Aprendimos que LINQ siempre devuelve una estructura de colección similar 
a cualquier otra lista. Sin embargo, la consulta LINQ no se ejecuta hasta que su resultado es 
accedido por alguna otra pieza de código, como el bucle "foreach" arriba. Esto es para 
permitirnos definir continuamente la consulta sin la sobrecarga re-evaluando 
cada nuevo paso en la consulta.

Proyecciones

Hasta ahora tan bueno; pero la mayoría de las veces, nuestras consultas necesitarán 
ser más complejas; así que vamos a tratar de proyectar datos. En SQL, Proyección significa seleccionar 
el nombre de la(s) columna(s) de la(s) tabla(s) que uno desea ver aparecer en el resultado 
de la consulta. En el caso de LINQ a Objects, se realizará una Proyección 
en un tipo de resultado de consulta diferente al tipo de objeto que realizamos 
en la consulta. 

Hay dos tipos de proyecciones que podemos hacer. Podemos 
realizar una proyección basada en un tipo de objeto existente o ir por completo 
a la otra forma mediante el uso de tipos anónimos. El siguiente ejemplo es del primer 
tipo:

1
2
IEnumerable<CarOwner> QueryResult = from car in ListOfCars
3
                                    select new CarOwner { owner_name = car.owner };

En el código anterior, el tipo de resultado de la consulta se declara como 
<CarOwner>, que es diferente de <Car> , el tipo con el que se inicializa la variable 'ListOfCar'. Tenemos 
también que usé la palabra clave "new" y he hecho algunas tareas dentro de la curva 
de llaves. En el código anterior, el uso de "select" con la palabra clave "new" le indica al 
compilador para crear una instancia de un nuevo objeto 'CarOwner' para cada entrada en el resultado de la consulta.
También asignando valores al nuevo tipo hemos inicializado cada instancia 
de la clase 'CarOwner'.

No obstante, si aún no tienes un tipo definido para 
utilizar, todavía puedes realizar proyecciones utilizando tipos anónimos.

Proyecciones usando tipos anónimos

Sería una gran molestia si, para cada Proyección, fueras 
obligado a crear un nuevo tipo. Por eso, a partir de C# 3.0, soporte para tipos 
anónimos fue agregado al lenguaje. Un tipo anónimo se declara usando la palabra clave "var". 
Le dice al compilador que el tipo de la variable es desconocido hasta que 
se asigna por primera vez.

1
2
var QueryResult = from car in ListOfCars
3
                  select new { 
4
                      car_name = car.name, 
5
                      owner_name = car.owner 
6
                  };
7
8
foreach(var entry in QueryResult)
9
    Console.WriteLine(entry.car_name);

Lo anterior es un ejemplo de realizar una consulta con Anonymous. 
El único problema a tener en cuenta es que el compilador no 
permite devolver tipos anónimos de los métodos.

Acceder a las propiedades de un tipo anónimo es fácil. En Visual Studio 2008, el Código 
Completion/Intellisense también lista las propiedades expuestas por el tipo Anónimo.

Refinamiento de datos

Por lo general, como parte de la consulta LINQ, también necesitamos refinar el resultado de la 
consulta especificando una condición. Al igual que SQL, LINQ también usa la cláusula "where" 
para decirle al compilador qué condiciones son aceptables.

1
2
IEnumerable<Car> QueryResult = from car in ListOfCars
3
                               where car.name == "Subaru"
4
                               select car;

El código anterior demuestra el uso de la cláusula "where" y 
La condición a seguir. Para seguir definiendo múltiples condiciones, LINQ soporta 
las construcciones 'and' (&& amp) y 'or' (||). La parte "where" de la consulta debe ser siempre una 
expresión booleana, de lo contrario el compilador se quejará.

Order By

Al consultar objetos, es posible confiar en la consulta 
obtenida por ya está ordenada. Si ese no es el caso, LINQ puede hacerse cargo de eso 
mediante el uso de la cláusula "order by" que asegurará que el resultado de su consulta sea 
ordenado apropiadamente. 

1
2
IEnumerable<Car> QueryResult = from car in ListOfCars
3
                               orderby car.model
4
                               select car;

Si ejecutas el código anterior, verás que el resultado de la 
consulta está ordenada en orden ascendente. Puedes alterar el orden usando las palabras clave "ascending" y "descending", 
y además cambia el orden especificando más de un campo para ordenar 
por. El siguiente código muestra cómo:

1
2
IEnumerable<Car> QueryResult = from car in ListOfCars
3
                               orderby car.model descending
4
                               select car;

Agrupar

LINQ también permite agrupar el resultado de la consulta por el valor de una 
propiedad específica como se muestra en este ejemplo:

1
2
var QueryResult = from car in ListOfCars
3
                  group car by car.owner into carOwnersGroup
4
                  select carOwnersGroup.Key;

Como puedes ver, LINQ admite la cláusula "group by" para 
especificar qué objeto y por qué propiedad agrupar por. La palabra clave "into" será la que 
luego nos permite proyectar sobre un resultado de agrupación al que se puede acceder mediante la propiedad 
 "Key".

Joins

LINQ admite la unión de datos de diferentes colecciones en un solo 
resultado de la consulta. Puedes hacer esto usando la palabra clave "join" para especificar qué objetos 
vas a unir; y usar la palabra clave "on" para especificar la relación coincidente entre 
los dos objetos. 

Inicializando lista relacionada:

1
2
List<Car> ListOfCars = new List<Car>()
3
{
4
    new Car {name = "Mitsubishi", owner = "Jeff" , model = 2005},
5
    new Car {name = "Land Rover", owner = "Danny", model = 2001},
6
    new Car {name = "Subaru"    , owner = "Smith", model = 2003},
7
    new Car {name = "Toyota"    , owner = "Alex" , model = 1992},
8
    new Car {name = "BMW"       , owner = "Danny", model = 2006},
9
};
10
11
List<CarOwner> ListOfCarOwners = new List<CarOwner>()
12
{
13
    new CarOwner {owner_name = "Danny", age = 22},
14
    new CarOwner {owner_name = "Jeff" , age = 35},
15
    new CarOwner {owner_name = "Smith", age = 19},
16
    new CarOwner {owner_name = "Alex" , age = 40}
17
};

Query:

1
2
var QueryResult = from car in ListOfCars
3
                  join carowner in ListOfCarOwners on car.owner equals carowner.owner_name
4
                  select new {name = car.name, owner = car.owner, owner_age = carowner.age};

En el código anterior, utilizando un tipo anónimo, hemos unido 
los dos objetos en un solo resultado de consulta.

Jerarquías de objetos usando uniones grupales

Hasta ahora, hemos aprendido cómo podemos usar LINQ para construir una lista 
plana de resultados de consulta. Con LINQ, también es posible lograr una consulta jerárquica 
en el resultado utilizando "GroupJoin". En palabras simples, podríamos asignar objetos a 
propiedades de cada entrada con consulta LINQ.

1
2
List<Car> ListOfCars = new List<Car>()
3
{
4
    new Car {name = "Mitsubishi", owner = "Jeff" , model = 2005},
5
    new Car {name = "Land Rover", owner = "Danny", model = 2001},
6
    new Car {name = "Subaru"    , owner = "Smith", model = 2003},
7
    new Car {name = "Toyota"    , owner = "Alex" , model = 1992},
8
    new Car {name = "BMW"       , owner = "Danny", model = 2006},
9
};
10
11
List<CarOwner> ListOfCarOwners = new List<CarOwner>()
12
{
13
    new CarOwner {owner_name = "Danny", age = 22},
14
    new CarOwner {owner_name = "Jeff" , age = 35},
15
    new CarOwner {owner_name = "Smith", age = 19},
16
    new CarOwner {owner_name = "Alex" , age = 40}
17
};
18
19
var QueryResult = from carowner in ListOfCarOwners
20
                  join car in ListOfCars on carowner.owner_name equals car.owner into carsGroup
21
                  select new {name = carowner.owner_name, cars = carsGroup};
22
23
foreach(var carOwner in QueryResult) 
24
    foreach(var car in carOwner.cars)
25
        Console.WriteLine("Owner name: {0}, car name: {1}, car model: {2}", carOwner.name, car.name, car.model);

En el ejemplo anterior, la cláusula "Join" va seguida de "into". 
Esto difiere de la operación de unión anterior que vimos. Aquí, la cláusula "into" 
se utiliza para agrupar los automóviles por el propietario (en carsGroup) y asignar la agrupación a la 
propiedad "coches" del tipo anónimo.

Operadores de consultas estándar

Hasta ahora, todo lo que hemos visto ha sido respaldado por la sintaxis de C# 3.0. 
Sin embargo, todavía hay una gran cantidad de operaciones que C# 3.0 no realiza soporte. 
Los operadores de consulta estándar proporcionan capacidades de consulta que incluyen 
filtrado, proyección, agregación, clasificación y más. Por lo tanto, estas operaciones son compatibles 
como métodos de la biblioteca LINQ y se pueden ejecutar en el resultado de una consulta como se muestra en la 
siguiente captura de pantalla:

Estos operadores se enumeran a continuación para tu referencia.

Operadores Agregados

  • Sum: devuelve la suma de todas las entradas.
  • Max: devuelve la entrada con el valor máximo.
  • Min: devuelve la entrada con el valor mínimo.
  • Average: devuelve el valor promedio de la colección.
  • Aggregate: utilizado para crear una agregación personalizada
  • LongCount: cuando se trata de una colección grande, este método devolverá un valor hasta el valor más grande admitido por la clase "long"
  • Count: devuelve un "integer" para el conteo de elementos en la colección

Operadores de elementos

  • First: devuelve la primera entrada de la colección de resultados.
  • FirstOrDefault: si la colección está vacía, devolverá el valor predeterminado, de lo contrario devolverá la primera entrada de la colección.
  • Single: devolverá solo el elemento de la colección.
  • SingleOrDefault: si la colección está vacía, devolverá el valor predeterminado, de lo contrario devolverá solo el elemento de la colección
  • Last: devuelve la última entrada de la colección.
  • LastOrDefault: si la colección está vacía, devolverá el valor predeterminado, de lo contrario, devolverá la última entrada de la colección
  • ElementAt: devuelve el elemento en la posición especificada
  • ElementAtOrDefault: si está vacía, devolverá el valor predeterminado, de lo contrario, devolverá el elemento en la posición especificada

Establecer operadores relacionados

  • Except: similar a la combinación izquierda en SQL, devolverá las entradas de un conjunto que no existe en otro conjunto
  • Union: devuelve todas las entradas de ambos objetos
  • Intersect: devuelve los mismos elementos de cualquiera de los conjuntos.
  • Distinct: devuelve entradas únicas de la colección.

Operadores de Generación

  • DefaultIfEmpty: si el resultado está vacío, devuelve el valor predeterminado
  • Repeat: se repite en los objetos que se devuelven por el número especificado de veces
  • Empty: devolverá una colección IEnumerable vacía
  • Range: devuelve un rango de números para un número inicial y un conteo especificados

Operadores de refino

  • Where: devolverá objetos que cumplan con la condición especificada
  • OfType: devolverá objetos del tipo especificado

Operadores de conversión

  • ToLookup: devuelve el resultado como una búsqueda
  • ToList: devuelve el resultado como una colección de listas
  • ToDictionary: devuelve el resultado como un diccionario
  • ToArray: devuelve el resultado como una colección de Array
  • AsQueryable: devuelve el resultado como un IQueryable<T>
  • AsEnumerable: devuelve el resultado como un IEnumerable<T>
  • OfType: filtra la colección según el tipo especificado
  • Cast: se utiliza para convertir una colección de tipo débil en una colección de tipo fuerte

Operadores de particionamiento

  • Take: devuelve un número especificado de registros
  • Takewhile: devuelve un número especificado de registros mientras que la condición especificada se evalúa como verdadera
  • Skip: omite el número especificado de entradas y devuelve el resto
  • SkipWhile: omite el número especificado de entradas mientras que la condición especificada se evalúa como verdadera

Operadores cuantificadores

  • Any: devuelve verdadero o falso para una condición específica
  • Contains: devuelve verdadero o falso para la existencia del objeto especificado
  • All: devuelve verdadero o falso a todos los objetos que cumplen la condición especificada

Operadores Join 

  • Join: devuelve entradas donde las claves en conjuntos son las mismas
  • GroupJoin: se utiliza para crear objetos jerárquicos basados en una relación maestra y detallada

Operadores de Igualdad

  • SequenceEqual: devuelve true cuando las colecciones son iguales

Operadores de ordenamiento

  • Reverse: devuelve una colección invertida
  • ThenBy: usado para realizar una clasificación adicional
  • ThenByDescending: se utiliza para realizar una clasificación adicional en orden descendente
  • OrderBy: usado para definir un orden
  • OrderByDescending: se utiliza para definir el orden descendente

Operadores de Proyección

  • SelectMany: se utiliza para aplanar una colección jerárquica
  • Select: usado para identificar las propiedades a devolver.

Operadores de Concatenacion

  • Concat: se utiliza para concatenar dos colecciones.

¿Y ahora qué?

LINQ ha demostrado ser muy útil para consultar objetos, y la sintaxis similar a SQL hace que sea fácil 
de aprender y usar. Además, la gran cantidad de operadores estándar permite encadenar a varios operadores 
para realizar consultas complejas. En un seguimiento de este tutorial, revisaremos cómo se puede usar LINQ para 
consultar bases de datos y contenido XML...

Vende Scripts .NET y Componentes en CodeCanyon


CodeCanyon