Compartir a través de


Introducción a los operadores de consulta estándar

Los operadores de consulta estándar son las palabras clave y los métodos que forman el patrón LINQ. El lenguaje C# define palabras clave de consulta LINQ que se usan para la expresión de consulta más común. El compilador traduce expresiones mediante estas palabras clave a las llamadas de método equivalentes. Las dos formas son sinónimos. Otros métodos que forman parte del System.Linq espacio de nombres no tienen palabras clave de consulta equivalentes. En esos casos debe usarse la sintaxis del método. En esta sección se tratan todas las palabras clave del operador de consulta. El entorno de ejecución y otros paquetes NuGet agregan más métodos diseñados para trabajar con consultas LINQ en cada versión. Los métodos más comunes, incluidos los que tienen equivalentes de palabra clave de consulta, se tratan en esta sección. Para obtener la lista completa de los métodos de consulta admitidos por .NET Runtime, consulte la documentación de api System.Linq.Enumerable . Además de los métodos descritos aquí, esta clase contiene métodos para concatenar orígenes de datos, calcular un valor único de un origen de datos, como una suma, promedio u otro valor.

Importante

Estos ejemplos usan un origen de datos System.Collections.Generic.IEnumerable<T>. Los orígenes de datos basados en System.Linq.IQueryProvider usanSystem.Linq.IQueryable<T> orígenes de datos y árboles de expresión . Los árboles de expresión tienen limitaciones en la sintaxis de C# permitida. Además, cada origen de datos IQueryProvider, como EF Core puede imponer más restricciones. Compruebe la documentación del origen de datos.

La mayoría de estos métodos funcionan en secuencias, donde una secuencia es un objeto cuyo tipo implementa la IEnumerable<T> interfaz o la IQueryable<T> interfaz. Los operadores de consulta estándar proporcionan funcionalidades de consulta, como el filtrado, la proyección, la agregación, la ordenación y mucho más. Los métodos que componen cada conjunto son miembros estáticos de las Enumerable clases y Queryable , respectivamente. Se definen como métodos de extensión del tipo en el que operan.

La distinción entre IEnumerable<T> y IQueryable<T> determina cómo se ejecuta la consulta en tiempo de ejecución.

Para IEnumerable<T>, el objeto enumerable devuelto captura los argumentos que se pasaron al método . Cuando se enumera ese objeto, se emplea la lógica del operador de consulta y se devuelven los resultados de la consulta.

Para IQueryable<T>, la consulta se traduce en un árbol de expresiones. El árbol de expresiones se puede traducir a una consulta nativa cuando el origen de datos puede optimizar la consulta. Bibliotecas como Entity Framework traducen consultas LINQ en consultas SQL nativas que se ejecutan en la base de datos.

En el ejemplo de código siguiente se muestra cómo se pueden usar los operadores de consulta estándar para obtener información sobre una secuencia.

string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');

// Using query expression syntax.
var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };

// Using method-based query syntax.
var query2 = words.
    GroupBy(w => w.Length, w => w.ToUpper()).
    Select(g => new { Length = g.Key, Words = g }).
    OrderBy(o => o.Length);

foreach (var obj in query)
{
    Console.WriteLine($"Words of length {obj.Length}:");
    foreach (string word in obj.Words)
        Console.WriteLine(word);
}

// This code example produces the following output:
//
// Words of length 3:
// THE
// FOX
// THE
// DOG
// Words of length 4:
// OVER
// LAZY
// Words of length 5:
// QUICK
// BROWN
// JUMPS

Siempre que sea posible, las consultas de esta sección usan una secuencia de palabras o números como origen de entrada. En el caso de las consultas en las que se emplean relaciones más complicadas entre objetos, los siguientes orígenes que modelan una escuela se utilizan:

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}

Cada Student tiene un nivel académico, un departamento principal y una serie de puntuaciones. Un Teacher también tiene una propiedad City que identifica el campus donde el profesor imparte clases. Un Department tiene un nombre y una referencia a un Teacher que actúa como jefe del departamento.

Puede encontrar el conjunto de datos en el repositorio de origen.

Tipos de operadores de consulta

Los operadores de consulta estándar difieren en el tiempo de ejecución, en función de si devuelven un valor singleton o una secuencia de valores. Esos métodos que devuelven un valor singleton (como Average y Sum) se ejecutan inmediatamente. Métodos que devuelven una secuencia aplazan la ejecución de la consulta y devuelven un objeto enumerable. Puede usar la secuencia de salida de una consulta como secuencia de entrada en otra consulta. Las llamadas a métodos de consulta se pueden encadenar juntas en una consulta, lo que permite que las consultas se vuelvan arbitrariamente complejas.

Operadores de consulta

En una consulta LINQ, el primer paso es especificar el origen de datos. En una consulta LINQ, la from cláusula viene primero para introducir el origen de datos (students) y la variable de intervalo (student).

//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
                        select student;

La variable de intervalo es similar a la variable de iteración en un foreach bucle, excepto que no se produce ninguna iteración real en una expresión de consulta. Cuando se ejecuta la consulta, la variable de intervalo actúa como referencia a cada elemento sucesivo de students. Dado que el compilador puede deducir el tipo de student, no es necesario especificarlo explícitamente. Puede introducir más variables de intervalo en una let cláusula . Para obtener más información, vea let (Cláusula).

Nota:

Para orígenes de datos no genéricos como ArrayList, la variable de intervalo debe escribirse explícitamente. Para más información, consulte el procedimiento para consultar un objeto ArrayList con LINQ (C#) y Cláusula from.

Una vez obtenido un origen de datos, puede realizar cualquier número de operaciones en ese origen de datos:

Tabla de sintaxis de expresiones de consulta

En la tabla siguiente se enumeran los operadores de consulta estándar que tienen cláusulas de expresión de consulta equivalentes.

Método Sintaxis de expresiones de consulta de C#
Cast Use una variable de rango explícitamente tipada:

from int i in numbers

(Para obtener más información, vea from clause).
GroupBy group … by

-o-

group … by … into …

(Para obtener más información, vea cláusula de grupo).
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) join … in … on … equals … into …

(Para obtener más información, vea cláusula de unión.)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(Para obtener más información, vea cláusula de unión.)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(Para obtener más información, vea orderby clause).
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(Para obtener más información, vea orderby clause).
Select select

(Para obtener más información, vea select clause).)
SelectMany Varias cláusulas from.

(Para obtener más información, vea from clause).
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(Para obtener más información, vea orderby clause).
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

(Para obtener más información, vea orderby clause).
Where where

(Para obtener más información, vea where clause).

Transformaciones de datos con LINQ

Language-Integrated Consulta (LINQ) no solo trata de recuperar datos. También es una herramienta eficaz para transformar datos. Mediante una consulta LINQ, puede usar una secuencia de origen como entrada y modificarla de muchas maneras para crear una nueva secuencia de salida. Puede modificar la propia secuencia sin modificar los propios elementos mediante la ordenación y la agrupación. Pero quizás la característica más eficaz de las consultas LINQ es la capacidad de crear nuevos tipos. La cláusula select crea un elemento de salida a partir de un elemento de entrada. Se usa para transformar un elemento de entrada en un elemento de salida:

  • Combine varias secuencias de entrada en una sola secuencia de salida que tenga un nuevo tipo.
  • Cree secuencias de salida cuyos elementos constan de solo una o varias propiedades de cada elemento de la secuencia de origen.
  • Cree secuencias de salida cuyos elementos constan de los resultados de las operaciones realizadas en los datos de origen.
  • Cree secuencias de salida en un formato diferente. Por ejemplo, puede transformar datos de filas SQL o archivos de texto en XML.

Estas transformaciones se pueden combinar de varias maneras en la misma consulta. Además, la secuencia de salida de una consulta se puede usar como secuencia de entrada para una nueva consulta. En el ejemplo siguiente se transforman los objetos de una estructura de datos en memoria en elementos XML.


// Create the query.
var studentsToXML = new XElement("Root",
    from student in students
    let scores = string.Join(",", student.Scores)
    select new XElement("student",
                new XElement("First", student.FirstName),
                new XElement("Last", student.LastName),
                new XElement("Scores", scores)
            ) // end "student"
        ); // end "Root"

// Execute the query.
Console.WriteLine(studentsToXML);

El código genera la siguiente salida XML:

<Root>
  <student>
    <First>Svetlana</First>
    <Last>Omelchenko</Last>
    <Scores>97,90,73,54</Scores>
  </student>
  <student>
    <First>Claire</First>
    <Last>O'Donnell</Last>
    <Scores>56,78,95,95</Scores>
  </student>
  ...
  <student>
    <First>Max</First>
    <Last>Lindgren</Last>
    <Scores>86,88,96,63</Scores>
  </student>
  <student>
    <First>Arina</First>
    <Last>Ivanova</Last>
    <Scores>93,63,70,80</Scores>
  </student>
</Root>

Para obtener más información, vea Crear árboles XML en C# (LINQ to XML).

Puede usar los resultados de una consulta como origen de datos para una consulta posterior. En este ejemplo se muestra cómo ordenar los resultados de una operación de combinación. Esta consulta crea una combinación agrupada y luego ordena los grupos según el elemento categoría, que todavía está en el ámbito. Dentro del inicializador de tipos anónimos, una subconsulta ordena todos los elementos coincidentes de la secuencia de productos.

var orderedQuery = from department in departments
                   join student in students on department.ID equals student.DepartmentID into studentGroup
                   orderby department.Name
                   select new
                   {
                       DepartmentName = department.Name,
                       Students = from student in studentGroup
                                  orderby student.LastName
                                    select student
                   };

foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}
/* Output:
Chemistry
  Balzan     Josephine
  Fakhouri   Fadi
  Popov      Innocenty
  Seleznyova Sofiya
  Vella      Carmen
Economics
  Adams      Terry
  Adaobi     Izuchukwu
  Berggren   Jeanette
  Garcia     Cesar
  Ifeoma     Nwanneka
  Jamuike    Ifeanacho
  Larsson    Naima
  Svensson   Noel
  Ugomma     Ifunanya
Engineering
  Axelsson   Erik
  Berg       Veronika
  Engström   Nancy
  Hicks      Cassie
  Keever     Bruce
  Micallef   Nicholas
  Mortensen  Sven
  Nilsson    Erna
  Tucker     Michael
  Yermolayeva Anna
English
  Andersson  Sarah
  Feng       Hanying
  Ivanova    Arina
  Jakobsson  Jesper
  Jensen     Christiane
  Johansson  Mark
  Kolpakova  Nadezhda
  Omelchenko Svetlana
  Urquhart   Donald
Mathematics
  Frost      Gaby
  Garcia     Hugo
  Hedlund    Anna
  Kovaleva   Katerina
  Lindgren   Max
  Maslova    Evgeniya
  Olsson     Ruth
  Sammut     Maria
  Sazonova   Anastasiya
Physics
  Åkesson    Sami
  Edwards    Amy E.
  Falzon     John
  Garcia     Debra
  Hansson    Sanna
  Mattsson   Martina
  Richardson Don
  Zabokritski Eugene
*/

La consulta equivalente mediante la sintaxis del método se muestra en el código siguiente:

var orderedQuery = departments
    .GroupJoin(students, department => department.ID, student => student.DepartmentID,
    (department, studentGroup) => new
    {
        DepartmentName = department.Name,
        Students = studentGroup.OrderBy(student => student.LastName)
    })
    .OrderBy(department => department.DepartmentName);


foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}

Aunque puede usarse una cláusula orderby con una o varias de las secuencias de origen antes de la asociación, normalmente no se recomienda. Es posible que algunos proveedores LINQ no conserven ese orden después de la combinación. Para obtener más información, vea join (Cláusula, Referencia de C#).

Consulte también