55
Thiago Vidal www.thiagoacvidal.com

Palestra: LINQ via C#

Embed Size (px)

Citation preview

Thiago Vidal

www.thiagoacvidal.com

Introdução ao LINQ

Inferência de Tipos

Tipos Anônimos

Expressões Lambda

Métodos de Extensão

LINQ to SQL: Introdução

LINQ to SQL: Integração com o Visual Studio

LINQ Providers

29/03/2015LINQ via C# - .Net Coders 2

O que é LINQ?

Language Integrated Query

Biblioteca de consulta a dados

Incorporado ao .Net Framework 3.5, lançado com a versão 3.0 do C#

Por que LINQ é necessário?

Permite ao desenvolvedor criar expressões de consulta em qualquer fonte de dados

Sintaxe prática e enxuta

Escrever menos código facilita a manipulação de dados

29/03/2015LINQ via C# - .Net Coders 3

Uma query (ou consulta) é uma expressão que extrai dados de uma fonte de dados

Geralmente, as queries são escritas com uma linguagem específica para cada fonte de dados. Ex: SQL para BDs relacionais e XQuery para XML.

Não é vantajoso para o desenvolvedor ter de aprender uma linguagem nova para cada fonte de dados

É aí que entra o LINQ que fornece um modelo unificado (e simplificado) para manipular dados com o .Net

29/03/2015LINQ via C# - .Net Coders 4

Escrevendo a primeira query – LINQ to Objects

Uma query LINQ consiste de 3 operações distintas:

1. Obter o data source (fonte de dados)

2. Criar a query

3. Executar a query

29/03/2015LINQ via C# - .Net Coders 5

Exemplo: filtrando números pares

29/03/2015LINQ via C# - .Net Coders 6

A fonte de dados neste exemplo é um array de inteiros

Dois lados da query: o lado esquerdo (atribuição) e o lado direito (filtro)

Note que para fazer um filtro não basta apenas criar uma variável.

Construindo o filtro: Começa com a palavra-chave ‘from’

Crie uma variável (num) que referencia os elementos da fonte de dados

Especifique a fonte de dados (in arrNums)

O ‘where’ aplica alguma condição específica, embora não seja necessário

O ‘select’ seleciona os elementos filtrados

Um laço (foreach) irá iterar sobre o lado direito da query para imprimir seu resultado

29/03/2015LINQ via C# - .Net Coders 7

Para ser possível a iteração sobre os elementos de uma fonte de dados, é necessário que essa classe implemente a interface IEnumerable<T>. Os tipos primitivos do .Net Framework já implementam por padrão essa interface.

Então, a query é executada dentro de um foreach.

29/03/2015LINQ via C# - .Net Coders 8

IEnumerable<T> e Generics

As consultas LINQ são baseadas em Generics

Dois conceitos-chave:

Quando você cria uma instância de uma classe genérica, você substitui o ‘T’ pelo tipo instanciado. Por exemplo, ao criar uma List de string, o ‘T’ é substituído por ‘string’.

A interface genérica IEnumerable<T> permite a iteração sobre elementos de classes que a implementam

29/03/2015LINQ via C# - .Net Coders 9

Neste exemplo, é criada uma lista de Pessoas e a query seleciona todas as pessoas;

O tipo retornado pela consulta é um IEnumerable<Pessoa>

29/03/2015LINQ via C# - .Net Coders 10

É possível “omitir” a notação genérica pelo uso da palavra-chave var;

Ao referenciar uma variável com ‘var’ o compilador infere, ou seja, resolve emtempo de compilação o tipo daquela variável.

No exemplo a seguir será feito um filtro de pessoas do sexo feminino:

29/03/2015LINQ via C# - .Net Coders 11

29/03/2015LINQ via C# - .Net Coders 12

Regras de utilização:

Erros de compilação:

Declarar uma variável com var e não atribuir nenhum valor

Atribuir null a uma variável var

Declarar um atributo como var (use-o somente como variável local)

Recomenda-se usar var quando:

Queries LINQ

Tipos por referência

29/03/2015LINQ via C# - .Net Coders 13

Exemplos:

29/03/2015LINQ via C# - .Net Coders 14

É um recurso para encapsular propriedades a um objeto sem ter de criar um novo tipo (uma nova classe) para isso;

O nome do tipo é gerado pelo compilador e o tipo de suas propriedades é inferido por ele;

O tipo não é acessível no código-fonte;

As propriedades de um tipo anônimo são read-only

Não podem ser usados como atributos;

Um tipo anônimo é definido sempre com a palavra-chave var;

29/03/2015LINQ via C# - .Net Coders 15

Como criar um tipo anônimo:

Perceba que as propriedades de um tipo anônimo são imutáveis. Não é possível a escrita (set).

29/03/2015LINQ via C# - .Net Coders 16

Exemplo de utilização: projeções em expressões LINQ

O que é uma projeção?

A cláusula select pode ser utilizada para retornar um subconjunto específico das propriedades de cada objeto na fonte de dados muitas vezes diferente dos dados originais

Útil quando você quer guardar algumas informações de cada objeto em uma sequência

Select new {...};

29/03/2015LINQ via C# - .Net Coders 17

Exemplo: selecionar alunos com as maiores notas da turma (maior ou igual a 7)

29/03/2015LINQ via C# - .Net Coders 18

Exemplo: selecionar alunos que possuem uma dependência em Álgebra Linear

29/03/2015LINQ via C# - .Net Coders 19

Uma expressão lambda é uma função anônima com a qual é possível criar delegates ou árvores de expressão

Você pode usar uma expressão lambda como uma função local e passar argumentos ou retornar valores de funções

Muito útil para escrever consultas LINQ

Primeiro exemplo: lambda com delegate

29/03/2015LINQ via C# - .Net Coders 20

Os delegates são definidos em uma outra classe. Ou, se estiver usando no Main, defina-o fora dele:

Outro exemplo

29/03/2015LINQ via C# - .Net Coders 21

Delegates funcionam como referências a métodos e são base para criar eventos no .Net. São como protótipos de funções (C/C++) inclusive.

Nos exemplos anteriores, foi definido utilizando uma expressão lambda o delegate para multiplicar um número por ele mesmo. E depois, em uma variável armazena-se o resultado da chamada do delegate.

Para criar uma expressão lambda você lista parâmetros de entrada (caso tenha) no lado esquerdo do operador =>, e a expressão em si (o filtro) fica do lado direito.

Ex.: A expressão x => x * x especifica o parâmetro x que armazena o retorno de x * x.

Ex2: A expressão (x,y) => x – y especifica dois parâmetros x e y para ser realizada umasubtração. Os argumentos nesta chamada, segundo o exemplo anterior, são 10 e 5 (emsubtrairDelegate(10,5))

29/03/2015LINQ via C# - .Net Coders 22

Utilizando o mesmo array do primeiro exemplo, faremos uma expressão lambda para retornar os números ímpares deste array:

Perceba que a expressão fica bem mais enxuta em comparação às demais. O filtro é feito por meio do método de extensão Where.

29/03/2015LINQ via C# - .Net Coders 23

Na versão 3.0 do C# foi introduzida uma nova feature chamada métodos de extensão, que permite ao desenvolvedor acrescentar novos métodos em classes existentes sem ter de utilizar os recursos de herança, ou até implementar alguma modificação no arquivo original.

Como funciona? Os métodos de extensão são definidos como estáticos (em classes também estáticas), mas são chamados como os métodos de instância.

Ex.: criamos uma classe chamada MyExtensions e definimos nela um método ToURL, que transforma uma string qualquer em uma URL:

29/03/2015LINQ via C# - .Net Coders 24

O que acontece, na verdade, é que estamos adicionando um método à classe String do .Net. Para isso funcionar, basta escrever ‘this’ no parâmetro antes do nome da classe, como mostra o slide anterior.

Uma observação é que o IntelliSense identifica um método de extensão pela seta azul ao lado do nome do método. Podemos ver que os métodos do LINQ que utilizamos no dia a dia são de extensão, definidos na classe Enumerable.

29/03/2015LINQ via C# - .Net Coders 25

Apresentando outros métodos de extensão

Average: retorna a média dos valores em uma coleção

ElementAt: retorna o elemento em determinada posição

29/03/2015LINQ via C# - .Net Coders 26

ElementAtOrDefault: retorna o elemento em determinada posição, ou null, caso a posição seja inválida.

OrderBy, GroupBy:

Crie a classe Funcionario:

29/03/2015LINQ via C# - .Net Coders 27

Instancie 10 objetos da classe Funcionario e popule suas propriedades:

Crie uma lista de Funcionários, inicializando-a com esses objetos:

29/03/2015LINQ via C# - .Net Coders 28

GroupBy (com e sem o método de extensão)

Selecionamos os salários de funcionários em ordem crescente (default)

Quando é ordem crescente não é necessário escrever ascending

Em ordem decrescente: orderby (expressão) descending

29/03/2015LINQ via C# - .Net Coders 29

Aplicando o LIKE do SQL

Selecionar funcionárias que contenham “ita” no nome

29/03/2015LINQ via C# - .Net Coders 30

Agrupar funcionários por departamento

group (elementos) by (chave de agrupamento) into (variável de agrupamento)

Faça uma projeção definindo um tipo anônimo para retornar os dados filtrados (select new)

No exemplo será impresso o departamento e o total de funcionários por depto.

29/03/2015LINQ via C# - .Net Coders 31

Agrupar funcionários por departamento

group (elementos) by (chave de agrupamento) into (variável de agrupamento)

Faça uma projeção definindo um tipo anônimo para retornar os dados filtrados (select new)

No exemplo será impresso o departamento e o total de funcionários por depto.

29/03/2015LINQ via C# - .Net Coders 32

Exemplo de uma consulta sem LINQ

Exemplo de uma consulta com LINQ

29/03/2015LINQ via C# - .Net Coders 33

Nessa parte iremos demonstrar como utilizar consultas LINQ para manipular uma base de dados SQL Server.

Para isso, tenha instalado o SQL Server Management Studio 2008 ou superior.

29/03/2015LINQ via C# - .Net Coders 34

Estudo de caso: Apresentar em um gridview todos os filmes cadastrados em uma locadora

Conecte ao SQL Server;

Criação da Base de Dados

No Object Explorer, clique com o botão direito sobre a pasta Databases e escolha “New Database”

Caso não esteja visível, no Menu acima clique em View e depois escolhe a opção Object Explorer

Em Database Name digite: ProducoesArtisticas e depois confirme.

Selecione agora o banco ProducoesArtisticas, expanda-o no nível de pastas, e selecione a pasta Tables. Com o botão direito, clique em “New Table”

Crie a tabela “Filmes” com os campos FilmeID (int), Nome (varchar (50)), Ano (datetime), Preço (decimal (10,2)), Genero (varchar(15)).

Crie a tabela “Atores” com os campos AtorID (int), FilmeID (int), Nome (varchar (40))

Defina a chave primária de cada tabela: Filmes: FilmeID / Atores: AtorID (Set Primary Key...)

29/03/2015LINQ via C# - .Net Coders 35

Defina o incremento automático de inserção de registros na tabela

A cada registro inserido, o ID será incrementado de 1 em 1 ou conforme for editado em “Identity Increment”

29/03/2015LINQ via C# - .Net Coders 36

Relacionamento entre tabelas

Pode ser feito via diagrama relacional ou manualmente por uma query

Via diagrama relacional:

Na pasta Database Diagrams, confirme a criação de um novo diagrama

Selecione as duas tabelas e clique em Add

Faça a ligação entre as duas tabelas, puxando uma seta do campo FilmeID da tabela Filmes para o campo FilmeID da tabela Atores.

Feito isso, abrirá uma janela “Tables and Columns”. Defina o nome do relacionamento como FK_Atores_Filmes (atores que participaram de um filme).

Primary Key table: Filmes / Foreign Key table: Atores (campos FilmeID)

Via query: (cria-se uma referência de Filme na tabela de Atores)

29/03/2015LINQ via C# - .Net Coders 37

Crie um projeto do tipo Console Application com o nome: NetCoders.LINQtoSQL.Intro;

Abra a janela Server Explorer (Menu -> View -> Server Explorer) para configurar a conexão com o banco;

Com o botão direito clique em Data Connections -> Add Connection;

Mude o Data Source de “Microsoft SQL Server Database File (SqlClient)” para “Microsoft SQL Server”;

Escolha o seu servidor e a opção de login (Windows Authentication ou SQL Server Authentication);

Selecione o banco de dados ProducoesArtisticas;

Concluído o processo de configuração, a sua conexão deverá estar visível em Data Connections.

29/03/2015LINQ via C# - .Net Coders 38

Inserção de registros: pode ser via SQL Server ou pelo Visual Studio

Via SQL Server:

Popule a tabela de Filmes, depois a tabela de Atores:

Filmes

29/03/2015LINQ via C# - .Net Coders 39

Atores

Verificar na tabela de Filmes qual é o ID do Filme no qual esse ator esteve presente

Exemplo: Robbie Williams – Uma Noite no Museu 3 (FilmeID = 3)

Tom Cruise – Mission Impossible (FilmeID = 4), etc...

29/03/2015LINQ via C# - .Net Coders 40

Fazendo a junção (join) entre as tabelas Filmes e Atores, é possível reunir esta informação:

E o resultado será este:

29/03/2015LINQ via C# - .Net Coders 41

As classes DataContext definidos no namespace System.Linq implementam os métodos que permitem a aplicação interagir com os dados salvos no banco;

Essas classes podem ser automaticamente geradas pelo Visual Studio:

No Solution Explorer, clique com o botão direito sobre o projeto

Escolha a opção Add New Item e depois “LINQ to SQL Classes”

Guarde o modelo como ProducoesArtisticas

Arraste as tabelas do Server Explorer para a tela do desenho do modelo de dados, para obter um diagrama como esse:

29/03/2015LINQ via C# - .Net Coders 42

A partir do momento em que arrastamos as tabelas o Visual Studio realiza o mapeamento do banco de dados para a aplicação automaticamente

Verifique o código mapeado em ProducoesArtisticas.designer.cs

Fazer uma consulta LINQ acessando a base de dados SQL Server:

Instancie o modelo:

Agrupar os filmes por gênero

29/03/2015LINQ via C# - .Net Coders 43

Como resultado, percebe-se que as aplicações Console nem sempre são as melhores para a apresentação de dados para o usuário;

Então, crie um projeto Windows Forms com o nome: NetCoders.LINQtoSQL

Set as Startup Project...

Repita o mesmo procedimento dos slides anteriores para mapear o BD na aplicação

No Form1.cs[Design], arraste um DataGridView da ToolBox;

Name: dgFilmes

Form1_Load: duplo clique no form para gerar este evento

Neste evento, quando a aplicação for carregada, ela carregará o grid populado com os dados do banco

29/03/2015LINQ via C# - .Net Coders 44

Form1_Load:

Criação das colunas do grid

Instanciar o contexto

Aplicar a query: selecionar todos os filmes cadastrados

29/03/2015LINQ via C# - .Net Coders 45

Form1_Load:

Cria uma variável idLinha que representa cada linha na tabela

Dentro do foreach, preenchemos a tabela:

29/03/2015LINQ via C# - .Net Coders 46

Resultado:

29/03/2015LINQ via C# - .Net Coders 47

Fazer uma consulta LINQ to SQL com que realize uma junção das tabelas Atores e Filmes

Selecionar atores que tenham participado de algum filme

Modifique o foreach para imprimir o nome do ator e o nome do filme:

29/03/2015LINQ via C# - .Net Coders 48

Fazer uma consulta LINQ to SQL com que realize uma junção das tabelas Atores e Filmes

Modifique as colunas..

Resultado:

29/03/2015LINQ via C# - .Net Coders 49

Um LINQ Provider é uma implementação do “padrão LINQ” para uma fonte de dados específica;

O padrão LINQ define um conjunto de regras que explicita como os comandos LINQ são mapeados em invocações de métodos de um determinado tipo.

Nem sempre o próprio tipo introduz esses métodos; eles são definidos por meio de um Provider adaptado a implementar os métodos de extensão.

Ao longo dessa palestra, apresentamos o LINQ to Objects, com o qual é possível fazer consultas a coleções de dados em memória; e o LINQ to SQL, para fazer consulta a uma base de dados SQL.

29/03/2015LINQ via C# - .Net Coders 50

Esses são os LINQ Providers implementados no .Net Framework:

29/03/2015LINQ via C# - .Net Coders 51

Ponto de partida: a interface IEnumerable<T>

Ela fornece o método GetEnumerator() que permite a iteração entre os elementos de uma coleção.

Estratégias para expandir o LINQ para outras fontes de dados não mapeadas pelo .Net:

Adaptar uma API existente, definindo novas classes que podem ser usadas com os Providers LINQ existentes

Criar um novo conjunto de classes para permitir a reutilização dos métodos de extensão

Escrever um Provider LINQ capaz de trabalhar com árvores de expressões

Utilizado para converter código LINQ para outra linguagem

Outra estratégia é escrever em determinada classe novos métodos de extensão que realizam operações auxiliares (mais simples).

29/03/2015LINQ via C# - .Net Coders 52

Um LINQ Provider deve implementar métodos como Select, Where, Join, OrderBy(dentre outros) em uma classe genérica.

29/03/2015LINQ via C# - .Net Coders 53

Vantagens Sintaxe unificada de acesso a dados (desde que haja um provider que implemente o padrão

LINQ sobre essa fonte de dados);

Utilização simultânea de vários Providers: uma mesma expressão pode ter dados de várias fontes de dados;

Consultas fortemente tipadas: todas as expressões são compiladas, assim todos os possíveis erros são detectados em tempo de compilação;

Fácil extensão: caso queira uma nova fonte de dados interagir com LINQ, basta implementar um provider com os métodos definidos pelo LINQ;

Padronização, código limpo e de fácil manutenção

29/03/2015LINQ via C# - .Net Coders 39

Desvantagens

Queries maiores em SQL no LINQ acaba gerando um código menos performático

Curva de aprendizado para aprender LINQ

29/03/2015LINQ via C# - .Net Coders 40