O artigo é um “tour” pela evolução do ASP.NET nos últimos anos e também do acesso a dados com ADO.NET, chegando ao ADO.NET Entity Framework e LINQ. Para ver tudo isso na prática, migraremos um antigo exemplo escrito em ASP.NET 1.x (VS2003) para ASP.NET 3.5 (VS2008).
Para que serve
Lembrar como funcionavam os aplicativos ASP.NET antigamente, usando as tecnologias da época, e como resolver as mesmas necessidades usando as tecnologias mais atuais do Visual Studio e .NET Framework.
Em que situação o tema é útil
Todas as tecnologias aqui apresentadas são de fundamental interesse para todo desenvolvedor .NET. Além disso, as técnicas apresentadas são bastante úteis caso o leitor precise migrar projetos antigos e adotar uma arquitetura mais nova, robusta e promissora.
Resumo do DevMan
Neste artigo vamos aprender a olhar para o passado para entender o presente e futuro. Vamos migrar, parte por parte, uma aplicação ASP.NET de 6 anos atrás para o que há de mais novo no desenvolvimento com .NET e VS2008. Você aprenderá por tabela várias das novidades incluídas no IDE do VS nos últimos anos, bem como novidades introduzidas no framework, no ASP.NET, ADO.NET e claro, na linguagem C#.
Desde o lançamento do Visual Studio 2002 (codinome Rainer) e do .NET Framework 1.0, muita coisa mudou no que diz respeito ao desenvolvimento com .NET. O Visual Studio 2003 (codinome Everett), lançado logo a seguir, trouxe o suporte para o desenvolvimento com uma nova versão do framework, a 1.1.
Naquela época o desenvolvimento com ASP.NET Web Forms revolucionou o mundo. Finalmente, uma ferramenta visual e RAD permitiu criar aplicações para Web usando recursos de drag & drop de controles, usando um editor WYSIWYG (What You See Is What You Get), uma linguagem orientada a objetos para programar o code-behind, com separação entre interface e lógica. Desenvolvedores de aplicativos desktop, como VB6 e Delphi (ou mesmo .NET Windows Forms), puderam usar facilmente seus conhecimentos para desenvolver para Web, sem curva de aprendizado. Velhos e ruins tempos do CGI!
Há alguns anos, aqui mesmo, logo no começo da Revista WebMobile, edição número 2, escrevi um artigo bastante simples que demonstrava como dar os primeiros passos com a então tecnologia ASP.NET Web Forms (1.1) e Visual Studio 2003. Criamos um “Cadastro de Usuários”. O objetivo deste artigo é, mostrar para você amigo desenvolvedor, como a plataforma evoluiu e como você pode migrar seus antigos projetos para usar os novos recursos mais atuais oferecidos pelo .NET Framework, tomando como base esse aplicativo que comentei.
Voltando ao passado
Para entender o presente e se preparar para o futuro, precisamos voltar um pouco ao passado e rever como as coisas funcionavam naquela época. O aplicativo Cadastro de Usuários, na versão para ASP.NET 1.1, pode ser baixado no endereço de download deste artigo. Para rodar novamente a aplicação, utilizei aqui uma máquina virtual com IIS 5.1 e Windows XP Professional SP2, Visual Studio 2003 e SQL Server 2005 (apesar de na época ter usado o MS SQL 2000).
A única tabela do banco de dados tem o script mostrado na Listagem 1. Ela contém os principais e mais básicos atributos de um cadastro de usuários de um site. Para reproduzir este exemplo de migração, você pode executar esse script dentro de um banco de dados qualquer, por exemplo, o Northwind, usando o SQL Server Management Studio Express.
Listagem 1. Script para criação da tabela de usuários
create table USUARIOS
(LOGIN char(10) not null,
NOME varchar(40),
SENHA varchar(10),
EMAIL varchar(40));
alter table USUARIOS
add constraint USUARIOS_PK
primary key (LOGIN);
Criada a tabela, vamos examinar a aplicação ASP.NET. Ele se chama USUARIOS, e roda em um diretório virtual do IIS, que hospedei em C:\USUARIOS. A sua interface principal é mostrada na Figura 1. Aqui temos Labels e quatro TextBoxes, que servem para o usuário entrar com as informações a serem enviadas ao BD. Temos dois botões e também controles de validação, para validar por exemplo o email, campos requeridos etc.
Figura 1. Interface da aplicação ASP.NET Web Forms 1.x com Visual Studio 2003
Note que, abaixo do designer, temos alguns componentes ADO.NET para conexão com o MS SQL Server. Com o exemplo criado na época era simples, esses componentes de acesso foram colocados na mesma classe que define a interface, o que obviamente em uma situação real não seria uma boa prática. Na verdade, para aquela época, só o fato de não ter que adicionar código de acesso a banco mesclado com HTML já era uma excelente boa prática! (não tenho saudades do ASP).
Se falamos de ASP.NET 1.x, obviamente vamos acabar falando em ADO.NET. No caso do exemplo, usamos um SqlConnection para conectar ao SQL Server. Um SqlCommand possuía o comando para inserir os dados no BD, usando parâmetros:
INSERT INTO USUARIOS (LOGIN, NOME, SENHA, EMAIL)
VALUES (@LOGIN, @NOME, @SENHA, @EMAIL)
No código do botão Cadastrar, no evento Click, tudo o que precisávamos fazer era passar para os parâmetros do SqlCommand os valores digitados nos TextBoxes, como mostra a Listagem 2. Abrimos a conexão, chamamos o método ExecuteNonQuery do SqlCommand e pronto. O bloco try finally vai garantir que a conexão seja sempre fechada (nesse caso, devolvendo a conexão ao pooling).
Connection Pooling é o mecanismo que permite ao ADO.NET "reaproveitar" conexões ao banco de dados. Imagine a seguinte situação: um usuário acessa a aplicação, conectamos ao BD para extrair informações e a exibimos no formulário. A seguir, fechamos a conexão e devolvemos o resultado ao browser. Como aplicações Web são state-less (sem estado), se esse mesmo ou outro usuário se conectar à aplicação, uma nova conexão precisará ser restabelecida. Conectar ao BD a cada requisição de usuário é literalmente um "suicídio" em ambiente Web, onde uma aplicação pode ter centenas e até milhares de conexões simultâneas.
O ADO.NET resolve isso de forma bastante elegante: após a página ser enviada ao browser, a conexão com o BD não é liberada, mesmo que você tenha chamado explicitamente o método Close do SqlConnection. O ADO.NET guarda automaticamente a conexão em pool (imagine isso como uma espécie de cache de conexões). Ou seja, a conexão fica aberta com o banco de dados e persiste entre requisições. Quando outro usuário conectar na aplicação, o ADO.NET verifica se existe uma conexão disponível no pool e caso encontre, a utiliza. Com isso, todo o tempo necessário para localizar o servidor de BD, estabelecer uma conexão, autenticar um usuário e verificar permissões não será mais consumido a cada requisição.
E o melhor de tudo, você não precisa fazer nada para usar esse recurso, pois ele já é ativado por padrão. Criar um mecanismo de Connection Pooling "no braço" via código é algo extremamente complicado (infelizmente já tive que passar por esse esforço em uma ocasião). No ADO.NET já temos isso pronto no próprio framework.
O Connection Pooling só pode ser usado em ambiente multi-threading (uma aplicação Web, por exemplo), onde temos várias threads simultâneas processando solicitações clientes. Não faz sentido, por exemplo, usar Connection Pooling em uma aplicação Windows Forms tradicional.
Você pode ainda controlar como o ADO.NET trabalha com Connection Pooling, fazendo alguns ajustes na propriedade ConnectionString do SqlConnection. Podemos especificar alguns parâmetros, veja os principais:
Connection Lifetime = Tempo de vida, em segundos, que uma conexão deve ficar no pool desde a sua criação
Max Pool Size = Número máximo de conexões que podem ficar em pool
Min Pool Size = Número mínimo de conexões que devem ficar em pool
Pooling = Indica se o Connection Pooling está habilitado
Veja um exemplo de como poderíamos usar alguns desses parâmetros na string de conexão:
workstation id=ACER;user id=sa;data source=ACER;persist security info=False;initial catalog=Northwind;Pooling=True;Min Pool Size=50;Connection Lifetime=120;
Atenção: sempre use a mesma ConnectionString (com os mesmos valores para todos os parâmetros) em todos os objetos de conexão, para que compartilhem o mesmo Connection Pooling.
Novamente, observe que não nos preocupamos em colocar o código de acesso a dados em uma outra classe. Não que isso fosse um grande problema, visto que, pelo recurso de code-behind instituído pelo ASP.NET, já há uma separação entre interface e lógica (quem programou em ASP clássico, deve lembrar que não existia separação alguma – no máximo um “includezinho”).
Listagem 2. Código ADO.NET para inserir dados no BD com base nos campos do form
private void Button1_Click(object sender, System.EventArgs e)
{
sqlCommand1.Parameters["@LOGIN"].Value = tbLOGIN.Text;
sqlCommand1.Parameters["@NOME"].Value = tbNOME.Text;
sqlCommand1.Parameters["@SENHA"].Value = tbSENHA.Text;
sqlCommand1.Parameters["@EMAIL"].Value = tbEMAIL.Text;
sqlConnection1.Open();
try
{
sqlCommand1.ExecuteNonQuery();
Response.Redirect("Confirma.aspx?Login=" + tbLOGIN.Text);
}
finally
{
sqlConnection1.Close();
}
}
O outro SqlCommand apenas valida se o usuário já está incluído no banco de dados (com base no login informado). Essa verificação é feita em conjunto com um CustomValidator, que no servidor, usa ADO.NET para validar o login. Não vamos nos preocupar com essa parte da aplicação, pois não é o objetivo do artigo discutir validações (até porque muita pouca coisa mudou de lá para cá nesse sentido, como a inclusão de grupos de validação).
Vamos então, aos poucos, adaptar nossa antiga aplicação para um novo mundo de tecnologias, mais atuais e elegantes, usando melhores práticas. Muitas coisas aqui eu farei manualmente, o que servirá para você aprender melhor o que temos de novidades desde 2002/2003 até 2009/2010.
Migrando do ASP.NET 1.x (VS2003) para ASP.NET 3.5 (VS2008)
No Visual Studio 2008, vamos clicar em File > Open Project e abrir o arquivo do projeto C# chamado USUARIOS.csproj. Será aberto um assistente de conversão (Figura 2), que vai adaptar nosso projeto para rodar no Visual Studio 2008 e ASP.NET 2.0. Na primeira tela, apenas clique em Next. Na segunda tela, informe que você deseja fazer um backup dos fontes originais, o que é muito bom caso alguma coisa saia errado na importação. Finalize essa parte, que vai adaptar sua aplicação para rodar no ASP.NET 2.0 (que por padrão é a versão utilizada pelo Visual Studio 2005). Na sequência, você já será questionado se deseja atualizar o Web Site para o .NET Framework 3.5 (agora sim, a versão do VS2008). Prontamente, responda que sim (Figura 3).
Figura 2. Assistente converte a aplicação ASP.NET 1.x para 2.0
Figura 3. Assistente converte a aplicação ASP.NET 2.0 para 3.5
Finalize os assistentes, marcando a opção Show the conversion log para ver o log de “conversão” gerado durante todo esse processo. Pronto, agora clique em File > Open Web Site, aponte para o diretório C:\USUARIOS e você já pode rodar a aplicação com Ctrl+F5. Ok, a aplicação roda perfeitamente, mas praticamente não usa nenhum novo recurso do ASP.NET 2.0 nem 3.5. Vamos então fazer algumas alterações manualmente.
Code-Behind
Examinando o code-behind do formulário (Listagem 3), notamos algumas coisas interessantes que passaram despercebidas pelo assistente de migração. Veja que a classe do formulário de cadastro, chamada oportunamente de Cadastro, deriva de System.Web.UI.Page. Logo abaixo dessa declaração, temos a lista de todos os controles contidos na página, com seus respectivos nomes e tipos. Todos esses membros são, por padrão, protected. Note também que todos esses controles possuem uma declaração equivalente no arquivo ASPX. Por exemplo, para o Label1 do tipo Label declarado na classe code-behind, tempos o markup <asp:Label id="Label1" runat="server" ... > no ASPX.
Listagem 3. Declaração da classe do Web Form
public class Cadastro : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label Label1;
protected System.Web.UI.WebControls.Label Label2;
protected System.Web.UI.WebControls.Label Label3;
protected System.Web.UI.WebControls.Label Label4;
protected System.Web.UI.WebControls.Label Label5;
protected System.Web.UI.WebControls.Label Label6;
protected System.Web.UI.WebControls.TextBox tbLOGIN;
protected System.Web.UI.WebControls.TextBox tbNOME;
protected System.Web.UI.WebControls.TextBox tbSENHA;
protected System.Web.UI.WebControls.TextBox tbCONFIRMA;
protected System.Web.UI.WebControls.TextBox tbEMAIL;
protected System.Data.SqlClient.SqlConnection sqlConnection1;
protected System.Data.SqlClient.SqlCommand sqlCommand1;
protected System.Data.SqlClient.SqlCommand sqlCommand2;
protected System.Web.UI.WebControls.Button Button1;
Desenvolvedores Delphi devem se lembrar prontamente desta abordagem. O IDE da linguagem também declara na classe do form todos os componentes usados na interface, que também ficam presentes no arquivo DFM (uma espécie de ASPX).
Desde o Visual Studio 2005 (codinome Whidbey) essa abordagem mudou. Um problema que existia no ASP.NET 1.x era que, se por algum motivo o desenvolvedor mudasse manualmente o nome de um controle no ASPX, ou na classe code-behind, deveria fazer isso em ambos.
O C# 2.0, que acompanhou o Visual Studio 2005, institui o recurso conhecido como classes parciais (partial class). A ideia é simples, é possível dividir a implementação de uma mesma classe em vários arquivos, desde que a classe sempre leve o mesmo nome. O recurso é simples, mas as possibilidades que ele trouxe são infinitas. Por exemplo, podemos injetar código em uma classe existente sem recorrer a recursos da OO “pura”, como herança. O designer do Visual Studio pôde com isso separar o código de inicialização de componentes em dois arquivos, que no entanto pertencem a mesma classe (foi o fim do InitializeComponent).
E no ASP.NET, o que ganhamos com o uso de classes parciais? Provavelmente a mudança mais notória é a extinção dos membros protegidos na declaração da classe. Mas veja que, se você for codificar algo no code-behind, ainda poderá acessar esses membros! Só que eles não estão mais lá, estão apenas no ASPX. O ASP.NET faz algo muito inteligente, com a ajuda de classes parciais e herança, faz com que a classe Cadastro tenha acesso aos atributos compilados através da classe definida no ASPX. Então, tudo o que precisamos fazer é mudar a nossa classe para partial, como mostra a Listagem 4. Observe também que retiramos a classe de dentro do namespace padrão, USUARIO. Todos os controles de tela, e também componentes de acesso a dados, foram removidos da classe.
Listagem 4. Classe parcial
// Classe não está mais dentro do namespace
public partial class Cadastro : System.Web.UI.Page
{
// Todos os controles protected foram removidos daqui
Agora veja que todo o código que inicializa esses controles fica declarado no método InitializeComponent (Listagem 5), que é chamado a partir do evento OnInit. Esse conceito também existe em aplicações Windows Forms. Mais adiante vamos desmembrar esse código. Apesar desse recurso ainda existir no ASP.NET 2.0/3.5, se você criar um novo Web Site, ele não usará o conceito de InitializeComponent, logo, vamos dar um fim a ele.
Listagem 5. InitializeComponent servia para inicializar controles
private void InitializeComponent()
{
this.sqlConnection1 = new System.Data.SqlClient.SqlConnection();
this.sqlCommand1 = new System.Data.SqlClient.SqlCommand();
this.sqlCommand2 = new System.Data.SqlClient.SqlCommand();
this.Button1.Click += new System.EventHandler(this.Button1_Click);
//
// sqlConnection1
//
this.sqlConnection1.ConnectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=NORTHWIND;Integrated Security=True;";
//
// sqlCommand1
//
this.sqlCommand1.CommandText = "INSERT INTO USUARIOS (LOGIN, NOME, SENHA, EMAIL) VALUES (@LOGIN, @NOME, @SENHA, @" +
"EMAIL)";
this.sqlCommand1.Connection = this.sqlConnection1;
this.sqlCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LOGIN", System.Data.SqlDbType.VarChar, 10, "LOGIN"));
this.sqlCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@NOME", System.Data.SqlDbType.VarChar, 40, "NOME"));
this.sqlCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@SENHA", System.Data.SqlDbType.VarChar, 10, "SENHA"));
this.sqlCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@EMAIL", System.Data.SqlDbType.VarChar, 40, "EMAIL"));
//
// sqlCommand2
//
this.sqlCommand2.CommandText = "select 1 from USUARIOS where LOGIN = @LOGIN";
this.sqlCommand2.Connection = this.sqlConnection1;
this.sqlCommand2.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LOGIN", System.Data.SqlDbType.VarChar, 10, "LOGIN"));
this.Load += new System.EventHandler(this.Page_Load);
}
Página ASPX
No arquivo Cadastro.aspx, vamos fazer algumas mudanças para refletir as novidades do ASP.NET 2.0/3.5. Note que o projeto ASP.NET 1.x declara a diretiva Page, logo no início do arquivo, como mostrado na Listagem 6. Altere como mostrado na Listagem 7. Aqui substituímos o atributo Codebehind por CodeFile. AutoEventWireup passou para True. Inherits, que liga a classe da página ASPX à classe definida no C#, não leva mais o namespace.
Listagem 6. Diretiva Page no ASP.NET 1.x
<%@ Page language="c#" Codebehind="Cadastro.aspx.cs" AutoEventWireup="false" Inherits="USUARIO.Cadastro" %>
Listagem 7. Diretiva Page no ASP.NET 2.0/3.5
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Cadastro.aspx.cs" Inherits="Cadastro" %>
O próximo passo é alterar o Doctype ou Document Type Declaration (Declaração do Tipo de Documento), que informa ao validador do documento, qual versão de HTML/XHTML está sendo utilizada (veja a Listagem 8). Observe nessa mesma listagem como deve ser declarado a tag html. Ainda sobre as principais tags, retire o Method = “Post” do element form.
Listagem 8. Doctype e html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
No elemento body, retire o atributo MS_POSITIONING, que no exemplo atual, usa o formato GridLayout. Com isso, os controles são posicionados em coordenadas absolutas de tela, semelhante a uma aplicação Windows Forms. Não vamos mais utilizar essa abordagem, vamos mudar para FlowLayout. Logo, você deve retirar todos os atributos style (Listagem 9) de todos os controles que definem as posições absolutas para os controles. Por exemplo, para uma Label, o novo código fica como o da Listagem 10.
Listagem 9. Style definindo posicionamento absoluto
Listagem 10. Controles não têm mais estilo que define posição absoluta
<asp:Label id="Label2" runat="server">Login</asp:Label>
EventHandlers
Se você observar na Listagem 11, o evento Click do Button1 (o botão que dispara o cadastro) é associado a um manipulador (Event Handler), que na verdade é um método da nossa classe de code-behind (Button1_Click). Essa definição é feita via código, no InitializeComponent (Listagem 11). Note o operador += ao invés de =. Isso significa no C# que estamos adicionando um manipulador ao evento em questão, pois no .NET, um evento pode ser respondido por vários manipuladores (métodos). Você pode remover todo esse tipo de código que está no arquivo .CS. No ASP.NET 2.0/3.5, atribuímos o nome do manipulador do evento no arquivo ASPX, veja como fica o código do botão na Listagem 12.
Listagem 11. Event handler via código
this.Button1.Click += new System.EventHandler(this.Button1_Click);
Listagem 12. Evento onclick aponta para o método Button1_Click
<asp:Button id="Button1"
Text="Cadastrar"
onclick="Button1_Click">
</asp:Button>
Estilos
O IDE do Visual Studio 2008 trouxe um novo recurso muito útil, acessível pelo menu View>Manage Styles. Aqui você pode visualmente definir classes de estilo CSS (Figura 4). Podemos definir todos os atributos de uma classe, como fontes, cores, tamanhos etc. Aqui defini duas classes (Figura 5), chamadas respectivamente de Texto (usado nas Labels, como mostra a Figura 6) e Titulo (cabeçalho da página). Criados os estilos, basta você apontar a propriedade CssClass de cada controle desejado para a respectiva classe. Com isso, separamos e centralizamos a definição de estilo dos controles, sem replicar código para cada um. Uma outra opção seria usar Themes&Skins, novo recurso do ASP.NET 2.0.
Bom, não preciso dizer que nosso código ASPX ficou muito mais organizado, enxuto e otimizado, dentro dos novos padrões do ASP.NET. Para facilitar nossa vida até aqui, coloquei o código final completo da página ASPX na Listagem 13.
Figura 4. Definindo um estilo
Figura 5. Estilos definidos para a página
Figura 6. Definindo a classe CSS de uma Label
Listagem 13. Código final da página ASPX
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Cadastro.aspx.cs" Inherits="Cadastro" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server"><title></title>
<style type="text/css">
.Texto
{
font-family: Tahoma;
font-size: small;
font-weight: bold;
}
.Titulo
{
font-family: Tahoma;
font-weight: bold;
color: #0066FF;
}
</style>
</head>
<body>
<form id="Form1" runat="server">
<span class="Titulo">Cadastro de Usuários</span><br/>
<asp:Label id="Label2" runat="server" CssClass="Texto">Login</asp:Label>
<br/>
<asp:TextBox id="tbLOGIN" runat="server"></asp:TextBox>
<br/>
<asp:Label id="Label3" runat="server" CssClass="Texto">Nome</asp:Label>
<br/>
<asp:TextBox id="tbNOME" runat="server"></asp:TextBox>
<br/>
<asp:Label id="Label4" runat="server" CssClass="Texto">Senha</asp:Label>
<br/>
<asp:TextBox id="tbSENHA" runat="server" TextMode="Password"></asp:TextBox>
<br/>
<asp:Label id="Label6" runat="server" CssClass="Texto">E-mail</asp:Label>
<br/>
<asp:TextBox id="tbEMAIL" runat="server" Width="190px"></asp:TextBox>
<br />
<br/>
<asp:Button id="Button1" runat="server"
Text="Cadastrar" onclick="Button1_Click">
</asp:Button>
<input type="reset" value="Limpar"/>
</form>
</body>
</html>
Criando uma classe de acesso a dados (DAL)
Finalizados os trabalhos na interface, vamos cuidar do acesso a dados. Uma ótima prática ao desenvolver aplicações ASP.NET com ADO.NET é separar totalmente o código que acessa os dados do código que manipula a interface, que está no .CS de code-behind. Fazemos isso criando uma camada, que nada mais é que uma classe encarregada de se comunicar com o banco de dados, centralizando todo o código. A essa camada damos o nome de DAL (Data Access Layer).
No Solution Explorer, dê um clique de direita no nome do Web Site e clique em Add New Item. Escolha Class e dê o nome de “DAL.cs” (Figura 7). Implemente o código da classe como mostrado na Listagem 14. O que fizemos aqui foi praticamente trazer todo o código do InitializeComponent que estava no form (você pode removê-lo totalmente, junto com o OnInit), colocando o código de inserção em um método chamado Insert. Importamos os namespaces System.Data e System.Data.SqlClient para trabalharmos com ADO.NET.
Já que no Visual Studio 2008 usamos o C# 3.0, usamos alguns novos recursos da linguagem. Por exemplo, usamos Type Inference (var) para omitir a declaração do tipo da variável (o compilador “adivinha” o tipo com base na instância, criando um objeto fortemente tipado). Não confunda isso com variants das linguagens Win32, como Delphi e VB6. Além disso, usamos construtores estendidos, o que permite que inicializemos valores de propriedades de um objeto diretamente no seu construtor (não é overload não!). Veja um exemplo na construção do SqlCommand.
Uma outra boa prática foi retirar o código que define a string de conexão que estava hard-coded, colocando-a no Web.Config (Listagem 15). Um membro público na DAL usa a classe WebConfigurationManager para ler a entrada em runtime. E finalmente, usei Refactoring (novidade do VS2005) para renomear algumas variáveis para nomes mais compactos, como “com” e “cmd”. Pronto, retiramos o código de acesso a dados da interface. Como só temos uma tabela e só vamos inserir dados nela, não vamos criar outras classes DAL nem outros métodos, como Select, Update etc.
Figura 7. Criando uma classe de acesso a dados
Listagem 14. Classe DAL separa o acesso a dados
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
public class DAL
{
public String ConStr = WebConfigurationManager.
ConnectionStrings["ConStr"].ConnectionString;
public void Insert(
String LOGIN, String NOME,
String SENHA, String EMAIL)
{
var con = new SqlConnection(ConStr);
var cmd = new SqlCommand(){
CommandText = "INSERT INTO USUARIOS " +
"(LOGIN, NOME, SENHA, EMAIL) " +
"VALUES (@LOGIN, @NOME, @SENHA, @EMAIL)",
Connection = con};
cmd.Parameters.AddWithValue("@LOGIN", LOGIN);
cmd.Parameters.AddWithValue("@NOME", NOME);
cmd.Parameters.AddWithValue("@SENHA", SENHA);
cmd.Parameters.AddWithValue("@EMAIL", EMAIL);
con.Open();
try {
cmd.ExecuteNonQuery();
}
finally {
con.Close();
}
}
}
Listagem 15. String de conexão no Web.Config
...
<connectionStrings>
<add name="ConStr"
connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"/>
</connectionStrings>
</configuration>
Agora, no formulário, onde originalmente estava o código ADO.NET, incluimos o código da Listagem 16. Aqui instanciamos a DAL e chamamos o método Insert, passando os parâmetros para ele com base nos controles de tela. Um ponto importante é que o método manipulador do evento é agora protected, e não private, assim como o Page_Load.
Listagem 16. Formulário usa uma classe de acesso a dados para comunicação com BD
protected void Button1_Click(object sender, System.EventArgs e)
{
var D = new DAL();
D.Insert(tbLOGIN.Text, tbNOME.Text, tbSENHA.Text, tbEMAIL.Text);
}
Estamos quase lá. No Solution Explorer, dê um clique de direita no arquivo Cadastro.aspx e escolha Set as Start Page, o que vai configurá-la como página inicial do Web Site. Ainda no Solution Explorer, vamos apagar tudo o que não é mais necessário no ASP.NET 2.0/3.5. Remova (clique de direita + delete) os arquivos e diretórios indicados na Listagem 17. Com isso, o projeto deve ficar reduzido simplesmente ao que está mostrado na Figura 8. Esse é o conceito de Web Site, não precisamos de arquivo de projeto, tudo o que está no diretório faz parte do Web Site. Para rodar o Web Site, basta abrir o diretório e apertar F5. Nada de diretório virtual, nem IIS, o VS usa um servidor próprio local. Esse é um excelente novo recurso trazido pelo Visual Studio 2005. Claro, na hora de distribuir a aplicação, você vai precisar usar o aspnet_compiler ou o menu Build>Publish Web Site. Você já pode rodar o Web Site e testar nossa nova implementação para o VS2008 (Figura 9).
Listagem 17. Arquivos a serem removidos
_UpgradeReport_Files
BIN
OBJ
UpgradeLog.XML
USUARIOS.csproj
USUARIOS.csproj.user
Global.asax (como não escrevemos nada nele, pode ser excluído)
Global.asax.cs
Cadastro.aspx.resx
Confirma.aspx.resx
AssemblyInfo.cs
Confirma.aspx
Figura 8. Web Site, super compacto
Figura 9. Aplicação ASP.NET migrada para VS2008
Stored Procedures
A interface agora não precisa mais se preocupar com detalhes inerentes ao acesso a dados. Tanto que vamos aplicar uma ótima boa prática sem tocar nela. Vamos adaptar nossa aplicação para usar Stored Procedures, recurso indispensável se você quiser ganhar performance. Usando o SQL Management, crie uma SP chamada “INSERT_USUARIO” conforme Listagem 18. Depois, na DAL.cs, simplesmente mude o código que instancia o SqlCommand como mostrado na Listagem 19. Observe o recurso de Construtores Estendidos e Type Inference do C# 3.0. Pronto, pode rodar e testar.
Listagem 18. Script para criação da Stored Procedure
create procedure INSERT_USUARIO
(@LOGIN char(10),
@NOME varchar(40),
@SENHA varchar(10),
@EMAIL varchar(40))
as
insert into USUARIOS
values (@LOGIN,@NOME,@SENHA,@EMAIL)
Listagem 19. SqlCommand agora usa StoredProcedure
var cmd = new SqlCommand() {
CommandText = "INSERT_USUARIO",
CommandType = CommandType.StoredProcedure,
Connection = con };
ADO.NET Entity Framework
Até aqui tudo ótimo. Poderíamos fazer muitas dessas coisas, como uma DAL, usando o VS2003. Outros recursos, no entanto, só existem no Visual Studio 2005 / ASP.NET 2.0. Também já usamos novos recursos do Visual Studio 2008 e C# 3.0.
O que veremos agora é sem dúvida, um dos principais novos recursos do .NET Framework 3.5 e Visual Studio 2008, o ADO.NET Entity Framework. Ele basicamente é um framework de persistência e mapeamento objeto/relacional, criado pela MS, que bate de frente com o então muito popular NHibernate. Com ele, podemos criar aplicações mais robustas, orientadas a objetos, usando boas práticas e princípios básicos de engenharia de software. Indo direto ao ponto, vamos trabalhar a partir de agora com OBJETOS, e não mais TABELAS. Deixamos de trabalhar com SQL e bancos relacionais para trabalhar com LINQ. Nos preocupamos mais com as necessidades de nossos clientes do que com problemas de infra-estrutura. É claro, o ADO.NET vai continuar existindo por baixo, mas a ideia é que justamente você não o utilize mais. Não é tão complicado como parece, tanto que vamos aplicar esse novo recurso em nosso exemplo simples, com poucas linhas de código e alguns cliques.
No Solution Explorer, clique de direita e escolha Add New Item. Selecione a opção ADO.NET Entity Data Model (Figura 10). Mantenha o nome padrão para o modelo. Você será questionado se deseja colocar o modelo no diretório App_Code, responda que sim. Na próxima tela, escolha a opção Generate from database (Figura 11), pois já temos um BD pronto. Porém, não esqueça que para os próximos projetos, seguindo boas práticas da engenharia de software, você vai criar o modelo antes, e depois o banco de dados. Clique em Next e escolha a conexão com o banco Northwind.
Figura 10. Criando um ADO.NET Entity Data Model
Figura 11. Criando um modelo a partir de um banco existente
Na próxima tela vamos escolher quais objetos do banco de dados vamos utilizar, no caso a tabela USUARIOS. Clicando em Finish, o VS vai criar um modelo de classes (Figura 12), onde podemos ver as entidades com seus respectivos atributos que mapeiam a tabela que selecionamos.
Figura 12. Modelo de classes
Se você abrir o arquivo gerado Model.edmx, verá que ele na verdade é um arquivo XML que contém informações sobre o modelo de armazenamento (tabelas do banco de dados), o modelo conceitual (classes) e o mapeamento entre eles. Esses dados são representados respectivamente pelas sessões StorageModels, ConceptualModels e Mappings do arquivo EDMX.
Observe na Listagem 20 um fragmento do StorageModel, que representa a entidade USUARIOS. Observe que ela representa fielmente a tabela do BD, inclusive com tipos nativos do SQL Server, como varchar, e propriedades como MaxLength. Note também que eu fiz a importação da Stored Procedure. Na Listagem 21 temos o modelo conceitual, a definição da nossa classe chamada USUARIOS. As propriedades são as mesmas do StorageModel, porém, os tipos são os do .NET Framework (por exemplo, string). E finalmente, na Listagem 22, temos o mapeamento propriamente dito, ou seja, qual campo da tabela vai corresponder a um atributo da classe.
Usando o View>Model Browser, podemos visualmente editar esse mapeamento (dê um clique de direita em NorthwindModel>Entity Types>USUARIOS). O resultado é o mostrado na Figura 12. Esse é um dos pontos fortes do ADO.NET Entity Framework, pois se lembrarmos de outros frameworks de persistência e mapeamento O/R, vamos ver que muitos exigem que a configuração do mapeamento seja feita no braço diretamente em XML.
Listagem 20. Model.edmx: StorageModel
<EntityType Name="USUARIOS">
<Key>
<PropertyRef Name="LOGIN" />
</Key>
<Property Name="LOGIN" Type="char" Nullable="false" MaxLength="10" />
<Property Name="NOME" Type="varchar" MaxLength="40" />
<Property Name="SENHA" Type="varchar" MaxLength="10" />
<Property Name="EMAIL" Type="varchar" MaxLength="40" />
</EntityType>
<Function Name="INSERT_USUARIO" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="LOGIN" Type="char" Mode="In" />
<Parameter Name="NOME" Type="varchar" Mode="In" />
<Parameter Name="SENHA" Type="varchar" Mode="In" />
<Parameter Name="EMAIL" Type="varchar" Mode="In" />
</Function>
Listagem 21. Model.edmx: ConceptualModel
<EntityType Name="USUARIOS">
<Key>
<PropertyRef Name="LOGIN" />
</Key>
<Property Name="LOGIN" Type="String" Nullable="false" MaxLength="10" Unicode="false" FixedLength="true" />
<Property Name="NOME" Type="String" MaxLength="40" Unicode="false" FixedLength="false" />
<Property Name="SENHA" Type="String" MaxLength="10" Unicode="false" FixedLength="false" />
<Property Name="EMAIL" Type="String" MaxLength="40" Unicode="false" FixedLength="false" />
</EntityType>
Listagem 22. Model.edmx: Mapping
<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel.USUARIOS)">
<MappingFragment StoreEntitySet="USUARIOS">
<ScalarProperty Name="LOGIN" ColumnName="LOGIN" />
<ScalarProperty Name="NOME" ColumnName="NOME" />
<ScalarProperty Name="SENHA" ColumnName="SENHA" />
<ScalarProperty Name="EMAIL" ColumnName="EMAIL" />
</MappingFragment>
</EntityTypeMapping>
Figura 13. Mapeamento visual entre tabelas e objetos
Para adaptar nosso exemplo para utilizar a nova abordagem, com ADO.NET Entity Framework, precisamos de uma única alteração, já que nosso aplicativo está bem dividido em camadas. Basta ir até a DAL e modificar o método Insert, implementando-o como mostra a Listagem 23. Note que retiramos o código ADO.NET. Já na primeira linha instanciamos nossa classe de mapeamento, USUARIOS, usando construtores estendidos para inicializar os valores dos campos. Pode parecer muito estranho escrever LOGIN = LOGIN, mas o compilador é capaz de identificar que a primeira referência é ao atributo de uma classe, e a segunda representa o parâmetro passado ao método. A seguir, instanciamos o modelo de entidades e adicionamos o objeto User a ele. SaveChanges é autoexplicativo. Veja que não trabalhamos mais com SQL, nem tabelas, mas objetos puros. O ADO.NET EF se encarrega de persistir os dados no banco, gerando o SQL e toda comunicação necessária. A Figura 14mostra a aplicação em execução.
Listagem 23. Novo código de inserção, usando ADO.NET EF
public void Insert(
String LOGIN, String NOME,
String SENHA, String EMAIL)
{
NorthwindModel.USUARIOS User = new NorthwindModel.USUARIOS()
{
LOGIN = LOGIN,
NOME = NOME,
SENHA = SENHA,
EMAIL = EMAIL
};
NorthwindModel.NorthwindEntities DM =
new NorthwindModel.NorthwindEntities();
DM.AddToUSUARIOS(User);
DM.SaveChanges();
}
Figura 14. Aplicação ASP.NET usando ADO.NET Entity Framework
AJAX
Para finalizar nosso exemplo com chave de ouro, nada melhor que uma pitadinha de AJAX. Vamos permitir que o usuário clique em um botão que vai enviar os dados do formulário ao servidor, porém sem um postback. Será uma chamada assíncrona, usando AJAX. Para isso, dê um clique de direita no nome do Web Site no Solution Explorer e adicione um Web Service (Figura 15). Implemente o código da classe do WS como mostrado na Listagem 24. Note a simplicidade, tudo o que fazemos é criar um método Insert que recebe os já conhecidos parâmetros de cadastro e repassa à nossa DAL, que atualmente usa de fundo o ADO.NET Entity Framework. Repare no comentário que, para o serviço poder ser utilizado com AJAX, devemos descomentar o atributo imediatamente superior a declaração da classe.
Figura 15. Adicionando um Web Service
Listagem 24. Web Service recebe dados do cadastro e repassa ao ADO.NET EF
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
/// <summary>
/// Summary description for WebService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService {
public WebService () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public void Insert(
String LOGIN, String NOME,
String SENHA, String EMAIL)
{
var D = new DAL();
D.Insert(LOGIN, NOME, SENHA, EMAIL);
}
}
Agora coloque no topo do formulário um componente ScriptManager da guia AJAX Extensions da ToolBox. Esse componente coordena toda a comunicação AJAX da página. Abra o editor da sua propriedade Services e adicione um novo item como mostra a Figura 16.
Figura 16. Serviço configurado no ScriptManager
No form coloque um Input Button (HTML), que se chamará Button2, com o texto de “Cadastrar(AJAX)”. No ASPX da página, dentro da sessão head, abaixo da declaração dos estilos, adicione o código da Listagem 25. Aqui temos um código JS que basicamente captura os dados de tela e envia ao Web Service no servidor, de forma assíncrona, sem fazer postback. As funções Sucesso e Erro são rotinas de callback que são chamadas pelo engine do AJAX em caso de sucesso ou erro na operação, respectivamente. Pronto! É só mandar rodar e verificar o resultado final (Figura 17).
Listagem 25. Enviando dados ao servidor usando AJAX
<script type="text/javascript">
function pageLoad()
{
$addHandler( $get("Button2"), "click", Insert );
}
function Insert() {
WebService.Insert(
$get("tbLOGIN").value,
$get("tbNOME").value,
$get("tbSENHA").value,
$get("tbEMAIL").value,
Sucesso,Erro);
}
function Sucesso() {
alert("Usuário inserido com sucesso");
}
function Erro() {
alert("Erro ao inserir usuário");
}
</script>
Figura 17. Enviando dados ao servidor com AJAX
Conclusão
Esse exemplo foi uma viagem no tempo, aonde vimos que está tudo indo muito bem no universo .NET. Vimos como a plataforma evoluiu rapidamente em questão de poucos anos. Em 2003, usávamos ADO.NET exaustivamente para trabalhar com BD. Hoje, sequer precisamos usar SQL. Vimos como usar boas práticas de desenvolvimento, abstraindo detalhes, usando o melhor da orientação a objetos e desenvolvimento em camadas. E para finalizar, adicionamos AJAX ao aplicativo que, no começo, era um simples cadastro em ASP.NET 1.x.
Faça bom proveito dos conhecimentos adquiridos, sucesso!