Guia do artigo:

Para você que usará esta apostila, está subentendido que você conheça e já tenha instalado em seu computador o SQL Server e .NET Framework 3.5/4.0 ou Visual Studio 2008/2010.

Esta apostila será feita com base no LinqPad, um aplicativo grátis que tem na rede e pode ser baixado na URL https://www.linqpad.net/.

O banco de dados que usaremos é o Northwind que é um banco de dados exemplo da Microsoft. Este banco de dados tem que está instalado no SQL Server (pode ser o Express – Versão grátis) de sua máquina.

Conectando com Banco de Dados

Existem dois tipos de conexão, uma é direta no Banco de Dados, a outra é através do assembly gerado (DLL) que contém um ou mais DataContext (Classe que mapeia as tabelas do banco de dados).

Usaremos primeiramente a conexão direta com o banco de dados, para isso, com o LINQPad aberto clique em Add connection.

Na janela LINQPad, no combo Data Context, selecione Automatic.

LINQPad

Propriedades do grupo Options:

  • Pluralize EntitySet and Table properties – Coloca os nomes das tabelas no plural (acrescenta a letra “S”).
  • Capitalize property names – Coloca a primeira letra dos campos em maiúsculas
  • Include Stored Procedures and Funcion – Acrescenta na lista os nomes das procedures.

Realizando consultas básicas

Neste capítulo vamos realizar algumas consultas para conhecer a estrutura do LINQPad, mas antes vamos conhecer os tipos de consulta disponíveis.

  • C# Expression – expressão na estrutura do C# usando a estrutura a partir do from.
    from e in Shippers
    select e
  • C# Statement – expressão feita com variáveis e o uso do método Dump para executar
    var x = from e in Shippers select e;
    x.Dump();
  • C# Program – estrutura de classe contendo apenas métodos em C#
    void Main()
    {
        Console.Write("Exemplo de Consulta em Tabela de Empresas");
        var tabela = from e in Shippers
                     select e;
        tabela.Dump("Tabela de Empresas");
    }

O resultado das consultas acima é semelhante a imagem a seguir:

resultado das consultas

Classes Anônimas no .NET Framework 3.5

A partir do framework 3.0, foi introduzido as classes anônimas e o tipo var. Esse tipo de objeto recebe qualquer tipo de dados, só que ele só vai reconhecer o seu tipo quando for atribuído um valor.

tipo var

No código ao lado estamos vendo o intellisense da variável x que assume as propriedades e os métodos do tipo string que é o tipo referente a propriedade Text do formulário.

Este tipo de variável pode receber um tipo anônimo. Tipos anônimos fornecem uma maneira conveniente para encapsular um conjunto de propriedades somente leitura em um Simples objeto sem ter que primeiro definir um tipo explicitamente. O nome do tipo é gerado pelo compilador e não está disponível no nível do código fonte. O tipo das propriedades é inferido pelo compilador.

Veja o exemplo a seguir como se comporta a instância de um tipo anônimo e seu uso dentro do código C#.

instância de um tipo anônimo

Perceba que o objeto pessoa é uma instância de um tipo anônimo que contém duas propriedades: Nome e Idade. O intellisense do Visual Studio mostra os tipos que pessoa pode assumir.

Podemos fazer qualquer tipo anônimo, até listas, coleções, tipos enumerados entre outros, veja o exemplo abaixo de um LINQ acessando uma lista de tipo anônimo.

LINQ acessando
 uma
lista de tipo anônimo

O LINQ também retorna tipos anônimos, e são através deles que podemos selecionar os campos que desejamos ou fazer campos calculados em tempo de execução. Veja abaixo:

tipos anônimos

O método First() retorna o primeiro da lista, no caso foi uma lista do tipo anônima pois foi feito a partir do new{} que é a declaração base para um tipo anônimo.

Podemos fazer, como no exemplo abaixo, campos calculados que contém fórmulas, expressões entre outros.

void Main()
 {
     var clientes = from c in Customers
                 select new {
                     Codigo = c.CustomerID,
                     Nome = c.CompanyName,
                     Tamanho = c.CompanyName.Length
                 };
     Console.Write("O primeiro cliente é:" +
         clientes.First().Nome);    
     clientes.Dump();    
 }
Dica: Toda vez que desejamos fazer uma consulta com o LINQ e retornar apenas alguns campos ou campos calculados através do select new{} onde gera um retorno do tipo Anônimo, use o var para receber este valor.

Agregados

Agora vamos trabalhar com algumas funções agregadas que já conhecemos na estrutura do SQL-Ansi utilizando o LAMBDA e o LINQ.

No exemplo abaixo será exibido a quantidade de registros que satisfez a consulta realizada.

void Main()
{
    var clientes = from c in Customers
    select c;
    Console.Write(string.Format("Foram encontrados {0} registro(s).",clientes.Count()));
    clientes.Dump();    
}

Neste exemplo abaixo, foi feito uma consulta que retornasse quantos territórios faz parte da mesma região:

void Main()
{
    var Territorio = from c in Territories
      group c.RegionID by c.RegionID into agrupamento
      select new {
          CodigoDaRegiao = agrupamento.Key, Quantidade = agrupamento.Count()
      };
    
    Territorio.Dump();    
}

Outro exemplo quer retorna a lista completa separada pela região:

void Main()
{
    var clientes = from c in Territories
        group c by c.RegionID into agrupamento
        select agrupamento;
    
    clientes.Dump();    
}

Para saber quanto custou todos os Frentes das Vendas de cada Vendedor:

void Main()
{
    var vendas = from v in Orders
    group v.Freight by v.EmployeeID into grupo
    select new {
          Vendedor = grupo.Key,
          TotalFrete = grupo.Sum()
    };
    Console.Write(vendas.Count());
    vendas.Dump();
}

Aprimorando a consulta, e se desejarmos consultar todas as vendas com o nome do Vendedor que se encontra em outra tabela ordenado pelo maior somatório do frete.

void Main()
{
    var vendas = from venda in Orders
         join vendedor in Employees on venda.EmployeeID equals vendedor.EmployeeID
         group venda by new {venda.EmployeeID, vendedor.FirstName} into grupoVenda
         orderby grupoVenda.Sum(o => o.Freight) descending
         select new {
             CodigoVendedor = grupoVenda.Key.EmployeeID,
            NomeVendedor = grupoVenda.Key.FirstName,
            MediaFrete = grupoVenda.Average(o=> o.Freight),
            MaiorFrete = grupoVenda.Max(o => o.Freight),
            MenorFrete = grupoVenda.Min(o => o.Freight),
            TotalFrete = grupoVenda.Sum( o => o.Freight),
            QuantFrete = grupoVenda.Count()
                };    
    vendas.Dump();    
}
Aprimorando a consulta

Filtros

Neste capítulo veremos exemplos de como realizar filtros no LINQ para retornar os dados desejados. Vamos iniciar procurando Clientes que são da Alemanha.

void Main()
{
    var clientes = from c in Customers
        where c.Country == "Germany"
        select c;
    
    clientes.Dump();
}

Clientes que são da Alemanha ou México:

void Main()
{
    var clientes = from c in Customers
        where c.Country == "Germany" || c.Country == "Mexico"
        select c;
    
    clientes.Dump();
}

Mesmo que o filtro anterior, acrescentando que o nome não pode exceder a 15 caracteres:

void Main()
{
    var clientes = from c in Customers
        where (c.Country == "Germany" || c.Country == "Mexico" ) &&
          c.CompanyName.Length<=15
        select c;
    
    clientes.Dump();
}
limitação do filtro

Retornar a lista de vendas realizadas, agrupadas por país, cujo a quantidade seja maior a 100:

void Main()
{
    var ListaNotas = from Notas in Orders
    group Notas by Notas.ShipCountry into grupo
    where grupo.Count() > 100
    select new {
       Pais = grupo.Key,
       Quantidade = grupo.Count()
    };    
    ListaNotas.Dump();
}
Retornar a lista de vendas realizadas

Ordenação

Sempre desejamos que nossas pesquisas tenham ordenação por um ou mais campos, ou até mesmo por campos calculados entre outras formas. Neste capítulo será dado alguns exemplos de ordenação.

Clientes em ordem alfabética crescente pelo nome da empresa:

void Main()
 {
     var clientes = 
         (
             from c in Customers
             orderby c.CompanyName // Ordenar pelo nome da empresa
             select new {c.CustomerID, c.CompanyName, c.ContactName, c.Country}
         ).Take(5);
         
     clientes.Dump();
 }

Ordenando pelo País depois pelo Nome da Empresa:

Ordenando pelo País depois pelo Nome da Empresa:
void Main()
 {
     var clientes = 
         (
             from c in Customers
             orderby c.Country, c.CompanyName
             select new {c.CustomerID, c.CompanyName, c.ContactName, c.Country}
         ).Take(5);
         
     clientes.Dump();
 }
resultado

Ordenando pelo País depois pelo comprimento do nome da Empresa:

void Main()
 {
     var clientes = 
         (
             from c in Customers
             orderby c.Country, c.CompanyName.Length
             select new {c.CustomerID, c.CompanyName, c.ContactName, c.Country}
         ).Take(5);
         
     clientes.Dump();
 }

Ordenando em ordem decrescente pelo nome do País e crescente pelo Nome do Contato:

void Main()
 {
     var clientes = 
         (
             from c in Customers
             orderby c.Country descending, c.ContactName
             select new {c.CustomerID, c.CompanyName, c.ContactName, c.Country}
         ).Take(5);
         
     clientes.Dump();
 }

Detalhamento (Downdrill)– Sabemos que os empregados possuem várias vendas, e não necessariamente precisamos entrar na tabela de Ordens para selecionar as vendas. Podemos localizar o empregado e dentro dele selecionar as vendas realizadas.

Vamos localizar o vendedor de sobrenome Davolio e listar as cinco primeiras vendas:

var emp = (from e in Employees
  where e.LastName.Contains("Davolio")
  select e).Single();
         
 var ord = from o in emp.Orders
  select o;
     
 Console.WriteLine("As vendas feitas por ({0} {1}) são:", 
 emp.FirstName, emp.LastName);            
 ord.Dump();
localizar o vendedor de sobrenome Davolio

Operadores de Conjuntos

Fazem parte dos operadores de conjuntos os: Distinct, Union, Intersect e Except.

Distinct (Distinto) – não aparece duplicado os dados dos campos selecionados.

Exemplo sem uso do Distinct:

void Main()
 {
     var clientes = from c in Customers
     where c.Region != null
     orderby c.Country , c.Region
     select new {c.Country, c.Region};
     clientes.Take(12).Dump();
 }
Exemplo sem uso do Distinct

Exemplo com Distinct:

void Main()
 {
     var clientes = (from c in Customers
     where c.Region != null
     orderby c.Country , c.Region
     select new {c.Country, c.Region}).Distinct();
     clientes.Take(12).Dump();
 }
Exemplo com Distinct:

Union – Serve para unir duas tabelas iguais (estrutura iguais) com resultados diferentes

Exemplo: Lista os 5 primeiros vendedores que a data de contratação seja feita no dia 17 independe de mês e ano, e todos os vendedores que sejam dos Estados Unidos, os campos listados serão: nome, < span class=”lf-badge”>>país e < span class=”lf-badge”>>data de contrato:

void Main()
 {
     var tabela1 = from e in Employees
                   where e.HireDate.Value.Day == 17
                   select e;        
     var tabela2 = from e in Employees
                   where e.Country == "USA"
                   select e;
     var resultado = from r in tabela1.Union(tabela2)
                     select new {r.FirstName, r.Country, r.HireDate};    
     resultado.Take(5).Dump();
 }
Lista os cinco primeiros vendedores que a data de contratação

Intersect – Faz a interseção entre duas tabelas retornando apenas as linhas que satisfazem ambas as tabelas.

No exemplo abaixo faremos a listagem de todos os vendedores que foram admitidos no dia 17, e que sejam de UK:

void Main()
 {
     var tabela1 = from e in Employees
                   where e.HireDate.Value.Day == 17
                   select e;        
     var tabela2 = from e in Employees
                   where e.Country == "UK"
                   select e;
     var resultado = from r in tabela1.Intersect(tabela2)
                     select new {r.FirstName, r.Country, r.HireDate};    
     resultado.Take(5).Dump();
 }

O Intersect também pode ser usado para realizar subquerys como no exemplo que segue em SQL Ansi e em LINQ.

No exemplo abaixo vamos visualizar todas as vendas (orders) feitas por vendedores (employee) cujo seu país seja Inglaterra (UK).

select * from Orders
where EmployeeID in (select EmployeeID from Employees where Country = 'UK')

Em LINQ ficará:

// Retorna a lista de Employee do país UK
var emp = (from a in Employees
where a.Country == "UK"
select a.EmployeeID);
 
// Lista todos as vendas pelos Employees de UK
var order = from o in Orders
where emp.Contains((int)o.EmployeeID)
select o;

Ou

var order = from o in Orders
where 
(from a in Employees
where a.Country == "UK" select a.EmployeeID
).Contains((int)o.EmployeeID)
select o;

Confira também