¿Que es LINQ?
Dejándonos ir por la documentación oficial de Microsoft, LINQ es el nombre de la tecnología que le da la habilidad al lenguaje de C# de manipular datos (Hacer queries).
¿Para qué nos sirve aprender LINQ?
Con LINQ tenemos una forma de hacer consultas (queries) sobre diferentes fuentes de datos (SQL, PostgreSQL, MongoDB y otros) utilizando un solo lenguaje de programación.
Con LINQ la barrera de entrada para desarrolladores nuevos que comiencen en nuestro proyecto es más baja ya que utilizamos sintaxis similar a la que ya utilizan cuando escriben en C# o Java.
Utilizando LINQ
Ahora vamos a comenzar a ver explicaciones con código.
Todos estos ejemplos van a estar en el repositorio de Github con los proyectos de este blog.
Para comenzar fui a visual studio y seleccioné el template de Console App en .NET CORE si no tienes Visual Studio puedes utilizar Visual Studio Code con las herramientas de .NET y crear un proyecto desde el CLI.
Cuando tengamos el proyecto creado vamos a ver una clase que nos generaron por defecto, la clase llamada Program.cs
Por ahora estaré utilizando la clase Program para enseñarles todos los ejemplos que estaremos viendo.
IEnumerable y IQueryable
Cuando utilizamos LINQ debemos pensar en las estructuras de datos que estaremos manipulando. Toda colección (agrupación de datos) que herede de la interface IEnumerable o IQueryable en C# puede ser manipulada con LINQ.
¿Qué quiero decir con esto?
Vamos a declarar una variable tipo int
y otra variable de tipo string
.
class Program
{
static void Main(string[] args)
{
string stringVariable;
int intVariable;
}
}
Luego de declarar las variables podremos hacer click + ctrl en la palabra string y esto nos llevara a la clase de String en el “namespace” de “System”. Si observamos esta clase podremos ver que hereda de la interface de IEnumerable, a eso los quería llevar, siempre y cuando estén utilizando una estructura que herede de IEnumerable o de IQueryable podremos utilizar LINQ sin embargo si intentamos utilizarlo en la variable de tipo int
la cual no hereda de esa interface veremos que no tenemos los métodos de LINQ disponibles.
String
Int
Consultando datos
Habiendo visto eso, vamos a borrar esas dos variables que declaramos y vamos a importar en la parte de arriba de nuestro Program.cs
dos “namespaces”
using System.Collections.Generic;
using System.Linq;
Ahora en el método de main
vamos a declarar una lista con nombres, vamos a utilizar esta lista para filtrar por nombres específicos utilizando LINQ.
static void Main(string[] args)
{
var customerList = new List<string>
{
"Cliente 1",
"Cliente 2",
"Cliente 3"
};
}
Ahora que tenemos nuestra colección podremos filtrarla utilizando la palabra where
, ósea el nombre de la lista.Where()
class Program
{
static void Main(string[] args)
{
var customerList = new List<string>
{
"Cliente 1",
"Cliente 2",
"Cliente 3"
};
var firstCustomer = customerList.Where(x => x == "Cliente 1").SingleOrDefault();
}
}
Como podemos ver en la línea de
var firstCustomer = customerList.Where(x => x == "Cliente 1").SingleOrDefault();
Estamos utilizando la palabra Where la cual utiliza sintaxis de lambda para buscar en la lista por el nombre de Cliente 1
y a lo último de la línea de código utilizamos el SingleOrDefault()
para ejecutar nuestra consulta. Esto nos traerá un el primer resultado que encuentre o nulo si no encuentra nada.
Ahora si ejecutamos nuestro programa y ponemos un breakpoint luego de la ejecución de nuestra última línea de código podremos ver el valor que nos trae nuestra consulta.
Y si le ponemos un valor arbitrario que no existe en la lista nos devolverá nulo.
Syntaxis
En el ejemplo que observamos arriba pudimos ver que la sintaxis que se utilizo para hacer nuestra consulta es bien parecida al código que ya escribimos en C#, LINQ tiene dos formas de ser escrito method syntax y query syntax. La sintaxis que utilice el desarrollador va a depender de su preferencia.
La diferencia entre una y la otra es que el method syntax es idéntico al código de C# y el query syntax es parecido a lo que escribiría en algo como SQL.
Veamos ejemplos de cada sintaxis.
Primero vamos a crear una clase en otro archivo llamada Person, la clase de person tendrá tres propiedades. El nombre de la persona, un identificador único y la edad.
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
Ahora volvemos a nuestro Program.cs y vamos a crear una lista de personas con valores predefinidos.
var peopleList = new List<Person>
{
new Person
{
Id = Guid.NewGuid(),
Name = "Genesis",
Age = 24
},
new Person
{
Id = Guid.NewGuid(),
Name = "Luis",
Age = 23
}
};
Ahora vamos a ver las diferencias entre las sintaxis haciendo una consulta contra esa lista de personas.
Query Syntax
En la consulta que estoy haciendo en el ejemplo de Query Syntax podemos ver que la sintaxis es muy parecida a la de SQL, estamos buscando en la lista de persona por la persona con la edad de 24 años y seleccionando solamente su nombre.
var ageOfGenesis = (from person in peopleList
where person.Age == 24
select person.Name
).SingleOrDefault();
Method Syntax
var ageOfLuis = peopleList.Where(x => x.Name == "Luis").Select(x => x.Name).SingleOrDefault();
En este caso ambos nos devuelven un String siempre y cuando exista en la colección con el nombre de la persona.
Con LINQ tenemos la habilidad de usar “reflection” una parte muy poderosa del lenguaje de programación C#. Con esto podemos darle forma a nuestros resultados de las consultas.
var genesis = (from person in peopleList
select new Person
{
Age = person.Age,
Name = person.Name,
}).FirstOrDefault();
var luis = peopleList.Select(x =>
new Person
{
Age = x.Age,
Name = x.Name
}).LastOrDefault();
Como podemos observar en ambas consultas estamos utilizando la palabra new Person
lo cual nos va a crear un objeto de tipo Person con los valores que le asignemos, en este caso los valores serán los del resultado de la consulta, solamente le estoy asignando valores a dos propiedades del objeto Person, la propiedad de Age
y la propiedad de Name
.
Filtrando datos
Como pudimos observar en las consultas anteriores, podemos filtrar utilizando la palabra Where
.
var filteredPeopleList = peopleList.Where(x => x.Age > 23 && x.Name != "Luis").ToList();
Podemos utilizar diferentes condiciones las cuales pueden ser o ciertas o falsas para filtrar resultados.
Podemos utilizar operadores como >
mayor que, <
menor que, ==
es igual, !=
no es igual...etc.
Agrupación
Podemos agrupar datos por campos que tengan en común. Por ejemplo en la consulta a continuación podemos observar que estamos agrupando los datos por edad, todas las personas que tengan la misma edad va a ser agrupadas en los resultados y devueltos en una lista de personas.
var groupedPeopleList = (from person in peopleList
group person
by person.Age
into grouped
select new Person
{
Age = grouped.Key,
Name = String.Join(",", grouped.Select(x => x.Name))
}).ToList();
En la agrupación cuando estamos utilizando reflection
para hacer nuestros resultados una lista de personas, podemos ver que la edad dice grouped.Key
esto va a asignarle el valor de la propiedad por la cual estamos agrupando a Age
y en la propiedad de Name
ya que no estamos agrupando por nombre vamos a enseñar en una lista separada por comas todos los nombres que pertenezcan a la agrupación de edades.
Orden
Ordenar datos es sencillo, podemos ver que al utilizar OrderBy
u OrderByDescending
podemos ordenar los datos de manera ascendente o descendiente por el campo que queramos.
var orderedPeopleByAgeAsclist = peopleList.OrderBy(x => x.Age).ToList();
var orderedPeopleByNameDesclist = peopleList.OrderByDescending(x => x.Name).ToList();