LINQ

Para este artigo estarei usando a versão Community 2015 do Visual Studio, .NET Framework 4.5 e SQL Server 2012. É necessário que você já tenha um conhecimento mínimo de linguagem C# e SQL.

Todos os exemplos são em Console Application, uma vez que a intenção é não é criar algo sofisticado, mas apenas elucidar os conceitos principais sobre o tema.

O que é LINQ?

LINQ (Language Integrated Query) ou Linguagem de Consulta Integrada é um recurso do .NET Framework que permite, através de algumas linguagens .NET, realizar consultas em estrutura de dados, base de dados, documentos XML, coleção de objetos, utilizando uma sintaxe semelhante a da linguagem SQL. Funciona como uma camada intermediária entre a fonte de dados e a linguagem.

Arquitetura LINQ

Fonte: msdn.microsoft

LINQ foi introduzido no Visual Studio 2008 na versão 3.5 do .NET Framework (faz tempo, não?) e desenvolvido pelo mesmo engenheiro de software responsável pela criação do Delphi e do C#, Anders Hejlsberg.

Lambda, sintaxe de consulta, sintaxe de método

Uma expressão lambda é a forma como a expressão de consulta é construída, tem sua origem no Cálculo Lambda, que oferece os fundamentos matemáticos.

c => (c + 2) > 5

O símbolo “=>” é o operador Lambda que é lido como “vai para (goes to)”, com a variável a esquerda e expressão a direita.

As sintaxes de consulta e de método são semanticamente idênticas, apesar de algumas pessoas acharem a primeira mais legível e fácil de compreender.

A sintaxe de consulta é criada usando operadores consulta padrão em LINQ e convertida em chamadas de método no momento da execução (CLR).

var consulta = from l in livros
            where l.genero == "Ficção Científica"
            orderby l.Titulo
            select l;

Na sintaxe de método as consultas são expressas como chamadas de métodos.

var consulta = livros
            .Where(l => l.geneto == "Ficção Científica")
            .OrderBy(m => m.Titulo);

Execução Imediata Vs Execução Adiada

Em LINQ uma consulta que retorna uma sequência de valores não é executada até que os dados sejam requisitados e acessados durante a iteração, isso é chamado de execução adiada ou deferred execution. Repare que no exemplo acima a variável não armazena o resultado (livros do gênero Ficção Científica), mas sim uma estrutura de dados chamada “Árvore de Expressão ou Expression Tree”, contendo informações sobre a origem dos dados (tabela, coleção, arquivo) , os comandos da consulta e qual resultado deve ser retornado.

Em contrapartida, consultas do tipo singleton, ou seja, que retornam um valor único são executadas imediatamente, como Count, First, Average, Min e Max. Para forçar a execução imediada, caso queira armazenar os resultados, você pode chamar os métodos ToList<T>, ToDictionary ou ToArray<T>. Exemplo:

var consulta = (from a in clientes where a.Cidade == "São Paulo").ToList();

LINQ Provider (LINQ to …)

É o LINQ Provider que vai nos permitir escrever as consultas para as operações do CRUD e existem 5 tipos:

  • LINQ to Objects
  • LINQ to XML(XLINQ)
  • LINQ to DataSet
  • LINQ to SQL (DLINQ)
  • LINQ to Entities

LINQ to Objects

Antigamente era necessário percorrer uma coleção através do laço foreach para recuperar algum elemento, agora só precisamos realizar uma consulta informando o que se deseja recuperar, para isso é preciso de uma referência ao namespace System.Data.Linq e que a coleção implemente a interface IEnumerable (ou IEnumerable<T>), como em Arrays, listas genéricas, dicionários, etc. Vejamos um exemplo:

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

namespace Providers
{
    class LINQToObject
    {
        static void Main()
        {
            // Fonte de dados
            int[] numeros = new int[] { 15, 20, 35, 40 };

            // Expressão de consulta
            IEnumerable<int> consulta = from n in numeros
                                     where n % 2 == 0
                                     select n;

            Console.Write("Números pares: ");

            // Executa a consulta
            foreach (int i in consulta)
            {
                Console.Write(i + " ");
            }

            Console.ReadLine();
        }
    }
}

O resultado será “Números pares: 20 40″. Notou a semelhança com SQL? Especificamos a fonte de dados “numeros” e uma variável “n” para indicar o elemento atual.

LINQ to XML

Para este exemplo crie um novo documento chamado artigos.xml e cole o código abaixo, será nossa fonte de dados. Em seguida mova para o diretório do projeto (csproj).

<?xml version="1.0" encoding="utf-8" ?>
<artigos>
   <artigo dia="17" mes="07" ano="2014">
      <id>1</id>
      <titulo>Análise Galaxy Tab S2 9,7</titulo>
	  <url>http://exemplo.com/artigo1</url>
	  <descricao>Desc 1</descricao>
   </artigo>
   <artigo dia="02" mes="05" ano="2014">
      <id>2</id>
      <titulo>Confira as novidades da CES 2014</titulo>
	  <url>http://exemplo.com/artigo2</url>
	  <descricao>Desc 2</descricao>
   </artigo>
   <artigo dia="02" mes="03" ano="2015">
      <id>3</id>
      <titulo>Fique por dentro de tudo que movimenta o setor de Cloud Computing</titulo>
	  <url>http://exemplo.com/artigo3</url>
	  <descricao>Desc 3</descricao>
   </artigo>
</artigos>

O que faremos a seguir é carregar nosso documento na memória e realizar a consulta, para essa tarefa duas classes vão nos auxiliar, XDocument e XElement, ambas do namespace System.Xml.Linq. Para o resultado da consulta criaremos uma lista de objetos do tipo Artigo.

using System;
using System.Linq;
using System.Xml.Linq;

namespace Providers
{
    class Artigo
    {
        public int Id { get; set; }
        public string Titulo { get; set; }
        public DateTime Data { get; set; }
        public string Url { get; set; }
        public string Descricao { get; set; }

        public Artigo(XElement artigo)
        {
            Id = Convert.ToInt32(artigo.Element("id").Value);

            Titulo = artigo.Element("titulo").Value;

            Data = new DateTime(
                    int.Parse(artigo.Attribute("ano").Value),
                    int.Parse(artigo.Attribute("mes").Value),
                    int.Parse(artigo.Attribute("dia").Value)
                );

            Url = artigo.Element("url").Value;

            Descricao = artigo.Element("descricao").Value;
        }
    }

    class LINQToXML
    {
        static void Main()
        {
                  // Carrega o arquivo xml
            XDocument xml = XDocument.Load(@"....artigos.xml");

            // Armazena cada nó/elemento do tipo artigo
            var artigos = xml.Elements("artigos").Elements("artigo");

            // Realiza a consulta filtrando os resultados
            var consulta = from p in artigos
                           where (int)p.Attribute("ano") == 2014 && p.Element("titulo").Value.Contains("Análise")
                           select new Artigo(p);

            // Percorre os resultados e exibe no console
            foreach (var artigo in consulta)
            {
                Console.WriteLine("Título: {0}nPublicado em: {1}nUrl: {2}nDescricão: {3}nn",
                    artigo.Titulo, artigo.Data.ToShortDateString(), artigo.Url, artigo.Descricao);
            }

            Console.ReadLine();
        }
    }
}

XDocument fornece os métodos Load para carregar o documento e Elements. O método Elements sempre irá retornar uma coleção dos elementos filhos do elemento passado como parâmetro, ou seja, se o nome do parâmetro é “artigos” os elementos filhos serão todos os do tipo “artigo”.

Nesta consulta filtramos e selecionamos todos os artigos do ano de 2014 que contém a palavra “Análise” na propriedade Value do elemento titulo. Após obter os resultados percorremos a lista e exibimos os dados no console.

LINQ to DataSet

Para o artigo não ficar mais extenso vamos criar um DataSet simples na mão via código, representando os produtos de um mercado.

using System;
using System.Linq;
using System.Data;

namespace Providers
{
    class LINQtoDataSet
    {
        static void Main()
        {
            // Objeto DataSet
            DataSet ds = new DataSet("Mercado");

            // Criando a tabela de produtos
            var dtProdutos = new DataTable("Produtos");
            dtProdutos.Columns.Add("Id", Type.GetType("System.Int32"));
            dtProdutos.Columns.Add("Nome", Type.GetType("System.String"));
            dtProdutos.Columns.Add("Preco", Type.GetType("System.Decimal"));
            ds.Tables.Add(dtProdutos);

            // Populamos a tabela com três produtos
            DataRow drProdutos = dtProdutos.NewRow();
            drProdutos["Id"] = 1;
            drProdutos["Nome"] = "Sabão em pó";
            drProdutos["Preco"] = 5.69;
            dtProdutos.Rows.Add(drProdutos);

            drProdutos = dtProdutos.NewRow();
            drProdutos["Id"] = 2;
            drProdutos["Nome"] = "Farinha de trigo";
            drProdutos["Preco"] = 3.29;
            dtProdutos.Rows.Add(drProdutos);

            drProdutos = dtProdutos.NewRow();
            drProdutos["Id"] = 3;
            drProdutos["Nome"] = "Macarrão Spaghetti";
            drProdutos["Preco"] = 5.55;
            dtProdutos.Rows.Add(drProdutos);
            
            var consulta =
                from produto in dtProdutos.AsEnumerable()
                select new
                {
                    Nome = produto.Field<string>("Nome"),
                    Preco = produto.Field<decimal>("Preco")
                };

            Console.WriteLine("-- Produtos --");
            foreach (var produto in consulta)
            {
                Console.WriteLine("{0} : R$ {1}", produto.Nome, produto.Preco);
            }

            Console.ReadLine();
        }
    }
}

Neste caso, sem muitas novidades. A classe de DataTable não implementa a interface IEnumerable<T>, portanto para realizar uma consulta LINQ precisamos chamar o método AsEnumerable e para acessar os valores das colunas utilizamos o método Field passando um parâmetro genérico que especifica o tipo de retorno da coluna.

LINQ to SQL

Antes de prosseguir utilize este script para gerar as tabelas no SQL Server.

Usar LINQ to SQL nos permite gerenciar dados relacionais como objetos por meio do mapeamento objeto-relacional O/RM, ou seja, as tabelas do banco de dados são representadas por classes e os registros de cada tabela como instâncias destas classes. Já as consultas criadas em sintaxe LINQ são convertidas em código T-SQL, a desvantagem é que só funciona com SQL Server 😦

Deste processo fazem parte três componentes:

  • LINQ to SQL API: envia o pedido de consulta para o LINQ to SQL Provider.
  • LINQ to SQL Provider: converte a consulta para T-SQL e envia ao ADO Provider.
  • ADO Provider: Após executar a consulta encaminha o resultado (DataReader) de volta ao LINQ to SQL Provider que converte em um objeto.

Para se comunicar com o banco faremos uso da classe DataContext, que é responsável por:

  1. Criar conexão com o banco de dados;
  2. Acessar dados;
  3. Converter objetos para consulta SQL e vice-versa.

Certo, mãos a obra:

Vamos realizar a conexão com o banco de dados clicando em View -> Server Explorer -> Data Connections -> Connect to Database.

linq_to_sql_01

Em seguida clique em Project -> Add New Item e escolha LINQ to SQL Classes, com o nome Empresa.dbml

linq_to_sql_02

Selecione as tabelas do banco e arraste para o DBML.

linq_to_sql_03

Adicione uma referência a System.Configuration e no arquivo App.config, renomeie a connection string para EmpresaConnectionString.

Precisamos agora “codificar a mágica” de interação com o banco:

using System;
using System.Linq;
using System.Configuration;

namespace Providers
{
    class LinqToSQL
    {
        static void Main(string[] args)
        {
            string connectionString = ConfigurationManager.ConnectionStrings["EmpresaConnectionString"].ConnectionString;

            // Objeto que herda as características de DataContext
            EmpresaDataContext db = new EmpresaDataContext(connectionString);

            //----------------------------------------------------------------------

            // Insert
            Funcionario novoFuncionario = new Funcionario();
            novoFuncionario.Nome = "Marcelo";
            novoFuncionario.Email = "marcelo@email.com";
            novoFuncionario.Endereco = "Rua Vergueiro, 44";
            novoFuncionario.FkProfissao = 4;

            // Insere um novo registro na tabela Funcionarios
            db.Funcionarios.InsertOnSubmit(novoFuncionario);

            //----------------------------------------------------------------------

            // Update
            
            // Busca um funcionário existente e atualiza o endereço
            var funcionario = db.Funcionarios.Where(a => a.Nome == "Carlos").FirstOrDefault();
            if (funcionario != null) funcionario.Endereco = "Rua Antônio Gomes, 22";

            //----------------------------------------------------------------------

            // Delete

            // Busca um funcionário existente e o remove 
            var exFuncionario = db.Funcionarios.Where(a => a.Id == 2).FirstOrDefault();
            if (exFuncionario != null) db.Funcionarios.DeleteOnSubmit(exFuncionario);

            //----------------------------------------------------------------------

            // Envia as alterações
            db.SubmitChanges();

            // Percorre a tabela de funcionários e exibe os dados no console
            foreach (Funcionario f in db.Funcionarios)
            {
                Console.WriteLine("Nome: {0}nEmail: {1}nEndereço: {2}nProfissão: {3}",
                                   f.Nome, f.Email, f.Endereco, f.Profissao.Nome);

                Console.WriteLine("n----------------------------------------------------------");
            }

            Console.ReadLine();
        }
    }
}

Linq To Entities

Antes de prosseguir utilize este script para gerar as tabelas no SQL Server.

Segue a mesma ideia do LINQ to SQL porém usando Entity Framework e dentre as vantagens temos a possibilidade de trabalhar com diferentes banco de dados como SQL Server, IBM DB2, Oracle, SQL Azure, etc.

Vamos lá, adicione uma referência a System.Data.Entity e em seguida clique em Project -> Add New Item e escolha ADO.NET Entity Data Model, com o nome Empresa.

linq_to_entities_01

Selecione a opção EF Designer from database.

linq_to_entities_02

Selecione a string de conexão com o banco de dados Empresa ou clique em New Connection para criar uma nova conexão, caso não exista.

linq_to_entities_03

Por fim selecione os objetos do banco de dados e clique no botão Finish.

linq_to_entities_04

Se tudo correu bem ao término do assistente veremos a janela do Entity Framework Designer exibindo o diagrama do banco de dados, através dele poderíamos modificar entidades, associações, mapeamentos e relacionamentos.

linq_to_entities_05

Legal, hora de partir para o código:

using System;
using System.Linq;

namespace Providers
{
    class LinqToEntities
    {
        static void Main()
        {
            using (var context = new EmpresaEntities())
            {
                // Insert
                Funcionario novoFuncionario = new Funcionario();
                novoFuncionario.Nome = "Lucas";
                novoFuncionario.Email = "lucas@email.com";
                novoFuncionario.Endereco = "Rua José de Azevedo, 422";
                novoFuncionario.FkProfissao = 3;

                // Insere um novo registro na tabela Funcionarios
                context.Funcionario.Add(novoFuncionario);

                //----------------------------------------------------------------------

                // Update

                // Busca um funcionário existente e atualiza o endereço
                var funcionario = context.Funcionario.Where(a => a.Nome == "Julia").FirstOrDefault();
                if (funcionario != null) funcionario.Email = "julia_123@email.com";

                //----------------------------------------------------------------------

                // Delete

                // Busca um funcionário existente e o remove 
                var exFuncionario = context.Funcionario.Where(a => a.Id == 5).FirstOrDefault();
                if (exFuncionario != null) context.Funcionario.Remove(exFuncionario);

                //----------------------------------------------------------------------

                var funcionarios = context.Funcionario.ToList();
                funcionarios.ForEach(a => Console.WriteLine("Nome:{0}nEmail:{1}nEndereço:{2}n", a.Nome, a.Email, a.Endereco));
    
                Console.ReadLine();
            }
        }
    }
}

Bom pessoal é isso, procurei abordar os aspectos principais, ainda poderia falar sobre o uso de delegates, mas prefiro deixar para um outro artigo, então bons estudos e até a próxima!

 

Deixe um comentário