C#: Consultas LINQ

1. Conceitos e motivação do LINQ

O LINQ (Language Integrated Query) é uma linguagem de consulta de dados integrada ao C# que permite consultar diferentes fontes de dados (coleções, bancos de dados, XML etc.) usando uma sintaxe unificada. Antes do LINQ, consultar diferentes fontes de dados exigia o aprendizado de diferentes APIs — o LINQ resolve esse problema.

▶ Exemplo

CSHARP
using System;
using System.Linq;

int[] numbers = { 5, 12, 3, 21, 8, 15 };

var result = from n in numbers
             where n > 10
             select n;

foreach (var n in result)
{
    Console.WriteLine(n);
}
▶ Experimente
TEXT 📖 Somente leitura
12
21
15

2. Sintaxe de consulta x sintaxe de método

O LINQ oferece dois estilos: sintaxe de consulta e sintaxe de método. Ambos são funcionalmente equivalentes. A sintaxe de método é mais comumente utilizada na prática, pois suporta mais operadores e permite chamadas encadeadas concisas.

▶ Exemplo

CSHARP
using System;
using System.Linq;

int[] numbers = { 5, 12, 3, 21, 8, 15 };

var querySyntax = from n in numbers
                  where n > 10
                  orderby n
                  select n;

var methodSyntax = numbers.Where(n => n > 10).OrderBy(n => n);

Console.WriteLine("Query syntax: " + string.Join(", ", querySyntax));
Console.WriteLine("Method syntax: " + string.Join(", ", methodSyntax));
▶ Experimente
TEXT 📖 Somente leitura
Query syntax: 12, 15, 21
Method syntax: 12, 15, 21

3. from / where / select / order by / group

As cláusulas principais da sintaxe da consulta: from especifica a fonte de dados e a variável de intervalo, where filtra, select projeta os resultados, orderby ordena e group ... by agrupa.

▶ Exemplo

CSHARP
using System;
using System.Linq;

string[] names = { "Alice", "Bob", "Anna", "Charlie", "Amy" };

var result = from name in names
             where name.StartsWith("A")
             orderby name.Length
             select name;

foreach (var name in result)
{
    Console.WriteLine(name);
}
▶ Experimente
TEXT 📖 Somente leitura
Amy
Anna
Alice

(1) Agrupamento por: Exemplo

CSHARP
using System;
using System.Linq;

var students = new[]
{
    new { Name = "Zhang", Grade = "A" },
    new { Name = "Li", Grade = "B" },
    new { Name = "Wang", Grade = "A" },
    new { Name = "Zhao", Grade = "C" },
    new { Name = "Sun", Grade = "B" }
};

var groups = from s in students
             group s by s.Grade into g
             select new { Grade = g.Key, Count = g.Count() };

foreach (var g in groups)
{
    Console.WriteLine($"Grade {g.Grade}: {g.Count} student(s)");
}
TEXT 📖 Somente leitura
Grade A: 2 student(s)
Grade B: 2 student(s)
Grade C: 1 student(s)

4. Operadores comuns

Operadores LINQ comumente usados na sintaxe de método:

Operador Finalidade
Onde Elementos filtrantes
Selecionar Projetar/transformar
OrderBy / OrderByDescending Ordenar em ordem crescente/decrescente
ThenBy Classificação secundária
GroupBy Agrupar elementos
First / FirstOrDefault Obter o primeiro elemento
Single / SingleOrDefault Obter o único elemento
Contagem Contagem de elementos
Qualquer Verificar se existe algum elemento
Todos Verificar se todos os elementos satisfazem a condição
Soma / Média Soma / média
Mín. / Máx. Mínimo / máximo
Pular / Aceitar Pular / aceitar o número especificado
Distinto Remover duplicatas
ToList / ToArray / ToDictionary Converter em coleção

▶ Exemplo

CSHARP
using System;
using System.Linq;

int[] numbers = { 3, 7, 2, 9, 5, 7, 3, 8 };

Console.WriteLine("Where > 5: " + string.Join(", ", numbers.Where(n => n > 5)));
Console.WriteLine("Select x2: " + string.Join(", ", numbers.Select(n => n * 2)));
Console.WriteLine("OrderBy: " + string.Join(", ", numbers.OrderBy(n => n)));
Console.WriteLine("OrderByDescending: " + string.Join(", ", numbers.OrderByDescending(n => n)));
Console.WriteLine("Distinct: " + string.Join(", ", numbers.Distinct()));
Console.WriteLine("First: " + numbers.First());
Console.WriteLine("FirstOrDefault > 10: " + numbers.FirstOrDefault(n => n > 10));
Console.WriteLine("Count > 5: " + numbers.Count(n => n > 5));
Console.WriteLine("Any > 8: " + numbers.Any(n => n > 8));
Console.WriteLine("All > 1: " + numbers.All(n => n > 1));
Console.WriteLine("Sum: " + numbers.Sum());
Console.WriteLine("Average: " + numbers.Average());
Console.WriteLine("Min: " + numbers.Min());
Console.WriteLine("Max: " + numbers.Max());
Console.WriteLine("Skip 3 Take 2: " + string.Join(", ", numbers.Skip(3).Take(2)));
▶ Experimente
TEXT 📖 Somente leitura
Where > 5: 7, 9, 7, 8
Select x2: 6, 14, 4, 18, 10, 14, 6, 16
OrderBy: 2, 3, 3, 5, 7, 7, 8, 9
OrderByDescending: 9, 8, 7, 7, 5, 3, 3, 2
Distinct: 3, 7, 2, 9, 5, 8
First: 3
FirstOrDefault > 10: 0
Count > 5: 4
Any > 8: True
All > 1: True
Sum: 44
Average: 5.5
Min: 2
Max: 9
Skip 3 Take 2: 9, 5

5. LINQ com List e Array

O LINQ funciona com qualquer objeto que implemente a interface IEnumerable<T>, incluindo List<T> e matrizes T[].

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;
using System.Linq;

List```<string>``` fruits = new List```<string>``` { "apple", "banana", "cherry", "date" };

var longNames = fruits.Where(f => f.Length > 5).Select(f => f.ToUpper());
Console.WriteLine("List query: " + string.Join(", ", longNames));

int[] scores = { 88, 72, 95, 60, 85 };
var topScores = scores.Where(s => s >= 85).OrderByDescending(s => s);
Console.WriteLine("Array query: " + string.Join(", ", topScores));
▶ Experimente
TEXT 📖 Somente leitura
List query: BANANA, CHERRY
Array query: 95, 88, 85

6. LINQ com Dictionary

Ao usar o LINQ em um dicionário, cada elemento é um KeyValuePair<TKey, TValue>, acessado por meio de .Key e .Value.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;
using System.Linq;

var prices = new Dictionary`<string, decimal>`
{
    { "Apple", 5.5m },
    { "Banana", 3.2m },
    { "Orange", 7.8m },
    { "Grape", 12.0m }
};

var expensive = prices.Where(kv => kv.Value > 5m)
                      .OrderByDescending(kv => kv.Value)
                      .Select(kv => $"{kv.Key}: {kv.Value}");

foreach (var item in expensive)
{
    Console.WriteLine(item);
}

var priceDict = prices.ToDictionary(kv => kv.Key, kv => kv.Value * 2);
Console.WriteLine("Doubled orange price: " + priceDict["Orange"]);
▶ Experimente
TEXT 📖 Somente leitura
Grape: 12.0
Orange: 7.8
Apple: 5.5
Doubled orange price: 15.6

7. Execução diferida x execução imediata

As consultas LINQ são diferidas por padrão: definir uma consulta não a executa — a consulta só é executada quando você itera sobre os resultados (por exemplo, com foreach). A chamada de métodos como ToList(), ToArray(), Count() ou First() aciona a execução imediata.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;
using System.Linq;

var numbers = new List```<int>``` { 1, 2, 3 };

var query = numbers.Where(n => n > 1);

numbers.Add(4);
numbers.Add(5);

Console.WriteLine("Deferred execution includes elements added later:");
foreach (var n in query)
{
    Console.WriteLine(n);
}

var immediate = numbers.Where(n => n > 1).ToList();

numbers.Add(6);

Console.WriteLine("Immediate execution does not include 6 added later:");
foreach (var n in immediate)
{
    Console.WriteLine(n);
}
▶ Experimente
TEXT 📖 Somente leitura
Deferred execution includes elements added later:
2
3
4
5
Immediate execution does not include 6 added later:
2
3
4
5

💡 A execução diferida significa que, ao iterar pelos resultados da consulta, os cálculos são refeitos a cada vez. Se você precisar usar os resultados várias vezes, chame ToList() para armazená-los em cache.


8. IQueryable x IEnumerable

IEnumerable<T> é usado para consultas a coleções na memória (LINQ to Objects), enquanto IQueryable<T> é usado para consultas a fontes de dados remotas (por exemplo, bancos de dados). Ele é capaz de converter árvores de expressões em linguagens de consulta, como SQL, e executá-las no lado do servidor.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;
using System.Linq;

List```<int>``` data = new List```<int>``` { 10, 20, 30, 40, 50 };

IEnumerable```<int>``` enumerableQuery = data.Where(n => n > 25);
IQueryable```<int>``` queryableQuery = data.AsQueryable().Where(n => n > 25);

Console.WriteLine("IEnumerable result: " + string.Join(", ", enumerableQuery));
Console.WriteLine("IQueryable result: " + string.Join(", ", queryableQuery));
Console.WriteLine("IQueryable expression: " + queryableQuery.Expression);
▶ Experimente
TEXT 📖 Somente leitura
IEnumerable result: 30, 40, 50
IQueryable result: 30, 40, 50
IQueryable expression: System.Collections.Generic.List`1[System.Int32].Where(n => (n > 25))

⚠️ Ao usar ORMs como o EF Core, IQueryable executa a filtragem no lado do banco de dados, enquanto IEnumerable carrega todos os dados na memória primeiro e só depois os filtra — a diferença de desempenho pode ser significativa.

❓ Perguntas Frequentes

P: O que é melhor — a sintaxe de consulta ou a sintaxe de método? R: A sintaxe de método é mais comumente usada, suporta todos os operadores e permite chamadas encadeadas concisas. A sintaxe de consulta suporta apenas alguns operadores, mas pode ser mais legível para operações complexas de junção/agrupamento.

P: Qual é a diferença entre FirstOrDefault e First? R: First lança uma exceção quando nenhum elemento é encontrado; FirstOrDefault retorna o valor padrão (null para tipos de referência, 0 para tipos de valor). É recomendável usar FirstOrDefault na maioria dos casos.

P: Qual é a diferença entre Single e First? R: Single exige que a sequência tenha exatamente um elemento correspondente — gera uma exceção se houver mais de um. First simplesmente retorna o primeiro elemento correspondente.

P: Por que iterar duas vezes pelos resultados do LINQ faz com que a consulta seja executada duas vezes? R: Devido à execução diferida, cada iteração recalcula os resultados. Use ToList() para armazenar os resultados em cache e evitar cálculos redundantes.

P: Como escolher entre IQueryable e IEnumerable? R: Use IQueryable para consultas a bancos de dados, de modo que o SQL seja executado no lado do servidor; use IEnumerable para coleções na memória.

📖 Resumo

📝 Exercícios

  1. Dado int[] nums = { 34, 7, 21, 56, 12, 89, 3 }, use a sintaxe do método para encontrar todos os números pares maiores que 20 em ordem decrescente e exiba o resultado
  2. Defina uma lista de alunos (nome, idade, turma), use o LINQ para agrupar por turma e exiba o número de alunos em cada turma
  3. Crie uma tabela Dictionary<string, int> para armazenar nomes de produtos e estoque; use o LINQ para localizar nomes de produtos cujo estoque seja inferior a 10 e converta-os para a tabela List<string>
  4. Escreva um código que demonstre a execução diferida: defina uma consulta e, em seguida, modifique a coleção original; observe se o resultado da iteração inclui novos elementos; depois, use ToList() para comparar com o comportamento da execução imediata
  5. Explique, em uma frase, por que IQueryable é mais eficiente do que IEnumerable no EF Core
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%