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