Compartilhar via


Uso do LINQ com o controle GridView do ASP.NET - Parte 2

Renato Haddad
Microsoft MVP, MCT, MCPD e MCTS
Fevereiro 2010

Tecnologias: Visual Studio .NET 2008 SP1, ASP.NET 3.5 e LINQ TO SQL

Sumário: Neste artigo vou mostrar um dos controles mais utilizados no ASP.NET, o GridView, o qual irá ser preenchido com dados oriundos de um banco de dados através do LINQ

Conteúdo

 Introdução
 Porque o LINQ to SQL?
 É possível usar o LINQ sem o ORM?
 Criação do projeto
 Bastidores do ORM
 Montar o GridView com LINQ
 Master / Detail
 Conclusão
 Referências + Tópicos Relacionados
 Sobre o Autor

Introdução

Na primeira parte deste artigo (https://msdn.microsoft.com/pt-br/library/ee518661.aspx) mostrei como usar o LINQ e o GridView  com um array e uma classe como fonte de dados. Neste artigo vou mostrar como criar um modelo de objeto relacional (ORM) com o Linq TO SQL e usá-lo na fonte de dados do GridView. O importante é você montar uma boa estrutura de banco de dados porque assim o ORM vai expressão exatamente as entidades, facilitando muito a codificação.

Para montar este exemplo abordado no artigo, você irá precisar do Visual Studio .NET 2008 com o SP1 instalado. O LINQ faz parte do .NET Framework 3.5 SP1 (Service Pack 1), portanto, se ainda não o tiver instalado, baixe do site de downloads da Microsoft e instale.

O público alvo são os desenvolvedores de aplicações ASP.NET.

Porquê o LINQ TO SQL?

O que é o LINQ e as vantagens você pode ler na parte 1 deste artigo, publicado no link que está na introdução. No entanto, pensando em banco de dados, hoje em dia temos duas possibilidades para modelar e gerar o ORM: O LINQ TO SQL (L2S) e o Entity Framework (EF). Irei usar o LINQ TO SQL porque é muito mais simples que o EF, pois para um artigo inicial o entendimento é melhor porque o L2S mostra as relações entre as tabelas claramente direto no ORM. Já o EF você precisa acessar a propriedade da classe e fazer referência à outra propriedade da classe relacionada, ou seja, isto você pegará com o tempo.

Tenho visto na prática muitos desenvolvedores utilizarem o Linq TO SQL justamente pela facilidade. Já o EF torna-se simples com a prática, pois tem muito mais recursos, e o L2S no .NET Framework 4 irá virar um subset do EF. Mas como eu disse, para um artigo inicial, não vou complicar o entendimento de vocês.

É possível usar o LINQ sem o ORM?

Perfeitamente sim. O LINQ é para manipular coleções, portanto, se você usa um modelo de desenvolvimento em camadas onde a camada de dados (DAL) retorna uma coleção, o LINQ se aplica como uma luva, além é claro de ser fantástico por deixar o código muito mais simples, claro e de fácil manutenção.

A criação do ORM é importante e facilitará muito o desenvolvimento de qualquer aplicação, não só SQL Server, mas você pode criar o ORM usando o Entity Framework para qualquer provedor que tiver o driver para o EF. Eu já fiz uma experiência com o EF e o Oracle usando um componente de terceiros que funcionou perfeitamente, portanto, não se prenda somente ao SQL Server.

Criação do projeto

O passo a passo para a criação do projeto e o formulário principal com os controles está na parte 1 do artigo listado no link da introdução. De qualquer forma, crie um projeto ASP.NET em C# chamado ArtigoLinqASPNET (Figura 1).

Figura 1 – Tipo de projeto

No formulário default.aspx adicione 3 botões (btnColecao, btnClasse e btnBanco) e 1 GridView chamado gridDados. Na Figura 2 você visualiza o design do formulário, sendo que o botão btnBanco será o foco deste artigo.

Figura 2 – Layout do formulário

O próximo passo é criar o ORM, portanto, no Solution Explorer, dê um clique com o botão direito no nome do projeto e selecione Add New Item. Na janela aberta, escolha o template LINQ to SQL Classes e digite Northwind.dbml no nome do arquivo a ser gerado, conforme a Figura 3. Cabe ressaltar que irei usar o banco de dados Northwind, mas nada o impede de usar qualquer outro DB.

Figura 3 – Arquivo LINQ TO SQL

Clique no botão Add. Por ser um projeto ASP.NET, para fins de organização do projeto, é sugerido que o arquivo a ser gerado seja inserido no folder App_Code, portanto, clique no botão SIM para aceitar que o folder seja criado. A seguir é preciso montar o ORM, portanto, no Server Explorer abra o banco de dados Northwind e mostre todas as tabelas. O que você deve fazer é selecionar apenas as tabelas Categories e Products, segure o CTRL para selecionar mais de uma tabela. Em seguida, arraste-as para dentro do Northwind.dbml para que seja criado o mapa das classes com as duas tabelas/entidades, conforme a Figura 4. A partir de agora, esqueça o termo tabela/campo, pois no ORM cada tabela torna-se uma classe e os campos são propriedades da classe. Portanto, use agora o termo classe e propriedade. Dê um CTRL + S para salvar o arquivo.

Figura 4 – Entidades no ORM

Bastidores do ORM

O que aconteceu nos bastidores do ORM quando o arquivo foi criado? Se você abrir o Solution Explorer irá notar que foi criado o arquivo Northwind.designer.cs o qual contém toda a classe chamada NorthwindDataContext com os códigos das classes Categories e Products com as respectivas propriedades e métodos necessários para qualquer tipo de operação de CRUD (Create, Read, Update e Delete). Veja na Listagem 1 a definição das classes. Observe que a NorthwindDataContext deriva de Linq.DataContext, ou seja, é o que chamaremos em todo o projeto, o contexto da classe. Note ainda que todas as classes são Partial Class, portanto, você pode estender qualquer uma destas classes. Uma dica: se precisar alterar qualquer coisa neste arquivo, jamais faça aqui, estenda a classe e implemente o que for preciso. Isto porque este arquivo é gerado automaticamente toda vez que qualquer alteração for feita no .dbml, por exemplo, incluir ou excluir uma nova tabela/entidade, excluir uma propridade, etc.

[System.Data.Linq.Mapping.DatabaseAttribute(Name="Northwind")]

public partial class NorthwindDataContext : System.Data.Linq.DataContext

[Table(Name="dbo.Categories")]

public partial class Category : INotifyPropertyChanging, INotifyPropertyChanged

[Table(Name="dbo.Products")]

public partial class Product : INotifyPropertyChanging, INotifyPropertyChanged

Listagem 1 – Código C# gerado

Montar o GridView com LINQ

Excelente, até agora já temos o formulário criado e o ORM definido, é tudo o que precisamos. Para efeito de novos códigos, pressione F7 para mostrar os códigos do formulário default.aspx e comente tudo o que estiver criado no Page_Load da página. A primeira coisa a fazer é instanciar o contexto da classe NorthwindDataContext logo após o partial class do formulário, conforme a Listagem 2. Desta forma, qualquer parte deste código iremos conseguir referenciar a classe.

public partial class _Default : System.Web.UI.Page

{

    // instancia o contexto da classe

    NorthwindDataContext ctx = new NorthwindDataContext();

Listagem 2 – Instanciar o contexto

Em seguida, volte para o formulário e dê um duplo clique no botão btnBanco para criar o respectivo evento. Agora vem a parte mais gostosa de codificar e quero chamar a atenção de todos com a quantidade de código que irei digitar para montar o gridView com todos os produtos. Pense em quantas linhas você faria com o ADO.NET normalmente. Então, veja na Listagem 3 o que é preicso para montar o gridDados com todos os produtos existentes na tabela Products. É isto mesmo, sei que você não está acreditando, são apenas duas linhas, sendo que na fonte de dados eu referenciei apenas o contexto e a classe Products.

protected void btnBanco_Click(object sender, EventArgs e)

{

    // mostrar todos os produtos

    gridDados.DataSource = ctx.Products;

    gridDados.DataBind();

}

Listagem 3 – Código para ler todos os produtos

OK, você não deve estar acreditando, então, pressione CTRL + F5 para executar o projeto, clique no botão Banco de Dados e veja o resultado com o grid mostrando todos os produtos, conforme a Figura 5. E aí, o que achou? Prefere fazer com o ADO.NET normal? Bem, nos bastidores do ctx.Products será feita a conexão com o banco de dados, a execução de um método que o compilador decide, por exemplo, um DataReader, para retornar todos os produtos da respectiva entidade, o fechamento da conexão com o banco, enfim, a nossa parte é só usar e abusar do ORM.

Figura 5 – Lista de produtos

Perceba que os dados foram mostrados sem nenhuma formatação, assim como todas as propriedades da classe Products. Então, vamos melhorar o código para mostrar apenas algumas propriedades. Eu vou mudar um pouco o código para um melhor entendimento. Conforme a Listagem 4, vou criar uma variável chamada produtos que é do tipo var (automaticamente será do tipo IQueryable) contendo a sintaxe obrigatória a padrão do LINQ. O from p in ctx.Products diz: para todos os itens contidos em Products, use a variável p para se refenciar a qualquer propriedade. Em seguida usei o select para selecionar cada uma das propriedades, sendo que o nome da categoria está na classe Categories, e através do CategoryID que é a chave, conseguimos ter acesso à outra propriedade de outra classe. Toda vez que for selecionar apenas algumas propriedades, o uso da palavra chave new é obrigatória. Ao final, a variável produtos será a fonte do gridDados.

protected void btnBanco_Click(object sender, EventArgs e)

{

    // mostrar todos os produtos

    var produtos = from p in ctx.Products

                   select new

                   {

                       p.ProductID,

                       p.Category.CategoryName,

                       p.UnitPrice,

                       p.UnitsInStock

                   };

    gridDados.DataSource = produtos;

    gridDados.DataBind();

}

Listagem 4 – Selecionar algumas propriedades

Execute novamente o formulário e conforme a Figura 6 irá notar que apenas algumas propriedades estão sendo mostradas.

Figura 6 – Lista alguns itens dos produtos

No C# 3 há um recurso chamado tipos anônimos o qual conseguimos renomear uma propriedade. Isto pode ser usado neste exemplo de forma que os nomes a serem mostrados no cabeçalho do gridDados seja o nome da propriedade definida no código. Sendo assim, veja na Listagem 5 como renomear as propriedades. Em seguida, execute novamente o projeto e confira.

var produtos = from p in ctx.Products

               select new

               {

                   codigo = p.ProductID,

                   categoria = p.Category.CategoryName,

                   preco = p.UnitPrice,

                   estoque = p.UnitsInStock

               };

Listagem 5 – Renomear as propriedades

Master / Detail

Uma situação muito comum que existe nas aplicações é o uso do Master/Detail, ou seja, você monta uma lista de itens pais e após selecionar um item, são pesquisados os filhos do mesmo. Neste modelo ORM que montamos é possível fazer isto com as categorias e os produtos. Para isto, no evento Page_Load vamos montar o seguinte código para que seja preenchida a dropModalidades com a lista de todas as categorias. Veja na Listagem 6 o devido código.

protected void Page_Load(object sender, EventArgs e)

{

    if (!Page.IsPostBack)

    {

        // monta o drop com as categorias

        var cat = from c in ctx.Categories select new { c.CategoryID, c.CategoryName };

        dropModalidades.DataValueField = "CategoryID";

        dropModalidades.DataTextField = "CategoryName";

        dropModalidades.DataSource = cat;

        dropModalidades.DataBind();

    }

}

Listagem 6 – Preenchimento do DropDownList com as categorias

Se você executar o formulário irá perceber que o dropModalidades irá exibir todos os nomes das categorias, afinal, quem define isto é a propriedade DataTextField. No entanto, quando você selecionar uma categoria, o item a ser capturado será o CategoryID que é o vínculo com a entidade de produtos.

Agora, dê um duplo clique no dropModalidades e comente todo o código que estiver escrito devido ao exercício anterior da parte 1 deste artigo.  A Listagem 7 mostra o código que você deverá digitar para filtrar somente os produtos da categoria selecionada. Primeiro criei uma variável chamada cat do tipo int que captura o conteúdo da categoria selecionada, ou seja, trará o código. Em seguida, usei o LINQ para filtrar com a clausula where somente os produtos da respectiva categoria ordenados (orderby) pelo nome do produto. E, ao final, o gridDados é montado com o resultado do filtro.

protected void dropModalidades_SelectedIndexChanged(object sender, EventArgs e)

{

    // captura a categoria selecionada

    int cat = Convert.ToInt16(dropModalidades.SelectedValue);

    // filtra os dados

    var dados = from p in ctx.Products

                where p.CategoryID.Equals(cat)

                orderby p.ProductName

                select new

                {

                    codigo = p.ProductID,

                    preco = p.UnitPrice,

                    estoque = p.UnitsInStock

                };

    // monta o grid

    gridDados.DataSource = dados;

    gridDados.DataBind();

}

Listagem 7 – Filtra os produtos da categoria selecionada

Para deixar o código perfeito, volte ao evento Page_Load e insira a seguinte linha após o preenchimento do dropModalidades para que dispare o evento que já filtra os produtos. Isto é preciso porque na primeira vez em que o formulário é carregado, é preenchido o dropModalidades, e como o primeiro item já é uma categoria, nada mais justo que mostrar os produtos.

// chama o método para filtrar os produtos da primeira categoria

dropModalidades_SelectedIndexChanged(null, null);

Veja na Listagem 8 o código complete do Page_Load.

protected void Page_Load(object sender, EventArgs e)

{

    if (!Page.IsPostBack)

    {

        // monta o drop com as categorias

        var cat = from c in ctx.Categories select new { c.CategoryID, c.CategoryName };

        dropModalidades.DataValueField = "CategoryID";

        dropModalidades.DataTextField = "CategoryName";

        dropModalidades.DataSource = cat;

        dropModalidades.DataBind();

        // chama o método para filtrar os produtos da primeira categoria

        dropModalidades_SelectedIndexChanged(null, null);

    }

}

Listagem 8 –Código completo do Page_Load

Salve e execute para ver o resultado. Uma opção interessante para você visualizar e navegar em todo o contexto é usar o debug no VS. Caso queira customizar o gridDados com colunas personalizadas fique à vontade, pois o foco é usar o LINQ para ler os dados das entidades no banco de dados.

Enfim, neste artigo você pode observar que o uso do ORM facilita e muito a vida do desenvolvedor, padroniza a escrita dos códigos, assim como a manutenção do mesmo. Se o projeto tiver o banco de dados SQL Server, praticamente eu não faço nenhum projeto hoje em dia que não use o LINQ TO SQL e o Entity Framework, a produtividade é um fator decisivo na entrega do produto.

Conclusão

Dedique um bom tempo para a definição do banco de dados, seus relacionamentos e a criação das entidades. Isto irá facilitar muito a criação, entendimento e manutenção do ORM, seja no LINQ TO SQL ou no Entity Framework. Além de tudo, usar tecnologias emergentes focada em produtividade irá torná-lo um profissional atualizado no mercado.

Referências + Tópicos Relacionados

Livro LINQ e C# 3.0 – A Solução em Consultas para Desenvolvedores – Autor: Renato Haddad http://www.editoraerica.com.br/buscafinal.asp?cod=2366

DVDs de treinamentos de LINQ TO SQL, Crie uma aplicação ASP.Net com LINQ e LINQ Avançado disponíveis no www.renatohaddad.com

Sobre o Autor

Renato Haddad (rehaddad@msn.com  – www.renatohaddad.com ) é MVP, MCT, MCPD e MCTS, palestrante em eventos da Microsoft em diversos países, ministra treinamentos focados em produtividade com o VS.NET 2008, ASP.NET 3.5, LINQ, Reporting Services e Windows Mobile. Visite o blog https://weblogs.asp.net/renatohaddad . Renato é autor do livro LINQ e C# 3.0 – A Solução em Consultas para Desenvolvedores http://www.editoraerica.com.br/buscafinal.asp?cod=2366