Compartilhar via


Visão geral dos operadores de consulta padrão

Os operadores de consulta padrão são as palavras-chave e os métodos que formam o padrão LINQ. A linguagem C# define palavras-chave de consulta LINQ que você usa para a expressão de consulta mais comum. O compilador converte expressões usando essas palavras-chave para as chamadas de método equivalentes. As duas formas são sinônimos. Outros métodos que fazem parte do System.Linq namespace não têm palavras-chave de consulta equivalentes. Nesses casos, você deve usar a sintaxe do método. Esta seção aborda todas as palavras-chave do operador de consulta. O runtime e outros pacotes NuGet adicionam mais métodos projetados para trabalhar com consultas LINQ a cada versão. Os métodos mais comuns, incluindo aqueles que têm equivalentes de palavra-chave de consulta, são abordados nesta seção. Para obter a lista completa de métodos de consulta compatíveis com o Runtime do .NET, consulte a documentação da System.Linq.Enumerable API. Além dos métodos abordados aqui, essa classe contém métodos para concatenar fontes de dados, computando um único valor de uma fonte de dados, como uma soma, uma média ou outro valor.

Importante

Esses exemplos usam uma fonte de dados System.Collections.Generic.IEnumerable<T>. Fontes de dados baseadas em System.Linq.IQueryProvider usam as fontes de dados System.Linq.IQueryable<T> e as árvores de expressão. As árvores de expressão possuem limitações na sintaxe C# permitida. Além disso, todas as fontes de dados IQueryProvider, como EF Core, podem impor mais restrições. Verifique a documentação da fonte de dados.

A maioria desses métodos opera em sequências, em que uma sequência é um objeto cujo tipo implementa a IEnumerable<T> interface ou a IQueryable<T> interface. Os operadores de consulta padrão fornecem recursos de consulta, incluindo filtragem, projeção, agregação, classificação e muito mais. Os métodos que compõem cada conjunto são membros estáticos das classes Enumerable e Queryable, respectivamente. Eles são definidos como métodos de extensão do tipo em que operam.

A distinção entre IEnumerable<T> e IQueryable<T> sequências determina como a consulta é executada em runtime.

Para IEnumerable<T>, o objeto enumerável retornado captura os argumentos que foram passados para o método. Quando esse objeto é enumerado, a lógica do operador de consulta é empregada e os resultados da consulta são retornados.

Para IQueryable<T>, a consulta é traduzida para uma árvore de expressão. A árvore de expressão pode ser traduzida para uma consulta nativa quando a fonte de dados pode otimizar a consulta. Bibliotecas como o Entity Framework convertem consultas LINQ em consultas SQL nativas que são executadas no banco de dados.

O exemplo de código a seguir demonstra como os operadores de consulta padrão podem ser usados para obter informações sobre uma sequência.

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

Sempre que possível, as consultas nesta seção usam uma sequência de palavras ou números como a origem de entrada. Para consultas em que relações mais complicadas entre objetos são usadas, as seguintes fontes que modelam uma escola são usadas:

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 tem um nível de escolaridade, um departamento primário e uma série de pontuações. Um Teacher também tem uma propriedade City que identifica o campus onde o docente ministra aulas. A Department tem um nome e uma referência a um Teacher que atua como chefe do departamento.

Você pode encontrar o conjunto de dados no repositório de origem.

Tipos de operadores de consulta

Os operadores de consulta padrão diferem no tempo de execução, dependendo se retornam um valor singleton ou uma sequência de valores. Os métodos que retornam um valor singleton (como Average e Sum) são executados imediatamente. Métodos que retornam uma sequência adiam a execução da consulta e retornam um objeto enumerável. Você pode usar a sequência de saída de uma consulta como a sequência de entrada para outra consulta. As chamadas para métodos de consulta podem ser encadeadas em uma consulta, o que permite que as consultas se tornem arbitrariamente complexas.

Operadores de consulta

Em uma consulta LINQ, a primeira etapa é especificar a fonte de dados. Em uma consulta LINQ, a from cláusula vem primeiro para introduzir a fonte de dados (students) e a variável de intervalo (student).

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

A variável de intervalo é como a variável de iteração em um foreach loop, exceto que nenhuma iteração real ocorre em uma expressão de consulta. Quando a consulta é executada, a variável de intervalo serve como uma referência a cada elemento sucessivo em students. Como o compilador pode inferir o tipo de student, você não precisa especificá-lo explicitamente. Você pode introduzir mais variáveis de intervalo em uma let cláusula. Para obter mais informações, consulte Cláusula let.

Observação

Para fontes de dados não genéricas, como ArrayList, a variável de intervalo deve ser explicitamente tipada. Para obter mais informações, consulte Como consultar um ArrayList com LINQ (C#) e a cláusula from.

Depois de obter uma fonte de dados, você poderá executar qualquer número de operações nessa fonte de dados:

Tabela de sintaxe de expressão de consulta

A tabela a seguir lista os operadores de consulta padrão que têm cláusulas de expressão de consulta equivalentes.

Método Sintaxe da expressão de consulta C#
Cast Use uma variável de intervalo explicitamente tipada:

from int i in numbers

(Para obter mais informações, consulte Cláusula from.)
GroupBy group … by

- ou -

group … by … into …

(Para obter mais informações, consulte 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 obter mais informações, consulte cláusula de junção.)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(Para obter mais informações, consulte cláusula de junção.)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(Para obter mais informações, consulte a cláusula orderby.)
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(Para obter mais informações, consulte a cláusula orderby.)
Select select

(Para obter mais informações, consulte a cláusula de seleção.)
SelectMany Várias cláusulas from.

(Para obter mais informações, consulte Cláusula from.)
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(Para obter mais informações, consulte a cláusula orderby.)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

(Para obter mais informações, consulte a cláusula orderby.)
Where where

(Para obter mais informações, consulte cláusula WHERE.)

Transformações de dados com LINQ

Language-Integrated Query (LINQ) não se trata apenas de recuperar dados. Também é uma ferramenta poderosa para transformar dados. Usando uma consulta LINQ, você pode usar uma sequência de origem como entrada e modificá-la de várias maneiras para criar uma nova sequência de saída. Você pode modificar a sequência em si sem modificar os próprios elementos classificando e agrupando. Mas talvez o recurso mais poderoso das consultas LINQ seja a capacidade de criar novos tipos. A cláusula select cria um elemento de saída de um elemento de entrada. Use-o para transformar um elemento de entrada em um elemento de saída:

  • Mescle várias sequências de entrada em uma única sequência de saída que tenha um novo tipo.
  • Crie sequências de saída cujos elementos consistem em apenas uma ou várias propriedades de cada elemento na sequência de origem.
  • Crie sequências de saída cujos elementos consistem nos resultados das operações executadas nos dados de origem.
  • Crie sequências de saída em um formato diferente. Por exemplo, você pode transformar dados de linhas SQL ou arquivos de texto em XML.

Essas transformações podem ser combinadas de várias maneiras na mesma consulta. Além disso, a sequência de saída de uma consulta pode ser usada como a sequência de entrada para uma nova consulta. O exemplo a seguir transforma objetos em uma estrutura de dados na memória em 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);

O código produz a seguinte saída 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 obter mais informações, consulte Criando árvores XML em C# (LINQ to XML).

Você pode usar os resultados de uma consulta como fonte de dados para uma consulta subsequente. Este exemplo mostra como ordenar os resultados de uma operação de junção. Esta consulta cria uma junção de grupos e classifica os grupos com base no elemento de categoria, que ainda está no escopo. Dentro do inicializador de tipo anônimo, uma subconsulta ordena todos os elementos correspondentes da sequência de produtos.

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
*/

A consulta equivalente usando a sintaxe do método é mostrada no código a seguir:

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}");
    }
}

Embora você possa usar uma orderby cláusula com uma ou mais das sequências de origem antes da junção, geralmente não a recomendamos. Alguns provedores LINQ podem não preservar essa ordenação após a junção. Para obter mais informações, consulte Cláusula join.

Consulte também