Partilhar 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 traduz 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 abrange todas as palavras-chave do operador de consulta. Os pacotes runtime e outros pacotes NuGet adicionam mais métodos projetados para trabalhar com consultas LINQ a cada nova 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 suportados pelo .NET Runtime, 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, calcular um único valor de uma fonte de dados, como uma soma, média ou outro valor.

Importante

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

A maioria desses métodos operam em sequências, onde 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 Enumerable classes 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 tempo de execução.

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 é convertida em uma árvore de expressão. A árvore de expressões pode ser convertida em 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 secção utilizam uma sequência de palavras ou números como fonte de entrada. Para consultas em que são usadas relações mais complicadas entre objetos, 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 grau, um departamento principal e vários resultados. A Teacher também tem uma City propriedade que identifica o campus onde o professor tem aulas. A Department tem um nome, e uma referência a um Teacher que serve como chefe de 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 sua execução, dependendo se eles 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. Os 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 range (student).

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

A variável range é 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 range serve como referência para cada elemento sucessivo no 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 range deve ser explicitamente digitada. Para obter mais informações, consulte Como consultar uma ArrayList com LINQ (C#) e from clause.

Depois de obter uma fonte de dados, você pode 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 digitada:

from int i in numbers

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

-ou-

group … by … into …

(Para obter mais informações, consulte a 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 a cláusula de adesã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 a cláusula de adesã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 SELECT.)
SelectMany Várias from cláusulas.

(Para obter mais informações, consulte a 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 a cláusula where.)

Transformações de dados com LINQ

Language-Integrated Query (LINQ) não se limita à recuperação de 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 a partir de um elemento de entrada. Você o usa 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 consistam 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. Essa consulta cria uma associação de grupo e, em seguida, classifica os grupos com base no elemento categoria, que ainda está dentro do 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 sintaxe de 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 sequências de origem antes da junção, geralmente não a recomendamos. Alguns provedores LINQ podem não preservar esse pedido após a adesão. Para obter mais informações, consulte cláusula de junção.

Ver também