ASP .NET - Criando uma enquete com LINQ e AJAX


Neste artigo eu mostro como criar uma enquete simples usando os recursos do LINQ to SQL e do AJAX.

Se você não conhece as tecnologias LINQ nem AJAX sugiro que você viste os links das referências para uma introdução ao assunto.

Nota:  Se você desconhece os conceitos sobre LINQ sugiro que leia os artigos da seção LINQ no site: ( LINQ)

Vou começar mostrando uma visão do projeto em tempo de desenho:

Podemos identificar os seguintes recursos no projeto:

  1. Os arquivos criado pelo LINQ to SQL para mapeamento OR/M;
  2. O banco de dados Pesquisas.mdf do SQL Server 2005 Express;
  3. As páginas do projeto e o arquivo web.config;
  4. O leiaute da página da enquete com os controles usados;
  5. O componente LinqDataSource;
  6. O ScriptManager para usar os recursos do AJAX;

Para acompanhar o construção do projeto usarei os seguintes recursos:

O objetivo do web site será efetuar uma enquete sobre quem será o campeão Paulista de 2009. Estou colocando as 4 opções existentes atualmente : Corinthians, Palmeiras, Santos e São Paulo. (até o dia 18 de abril de 2009 essas eram as opções)

Ao acessar a página da enquete o usuário pode selecionar uma opção e ao clicar no botão Vote será enviado para a página de resultado onde será exibida uma mensagem de agradecimento ou um aviso de que o usuário já votou. A página também possui um botão que permite visualizar o resultado em votos e percentuais.

Vamos iniciar criando um novo web site abrindo o VWD 2008 e no menu File selecionando new web site;

Selecione o template ASP .NET Web Site usando a linguagem C# e informe o nome enquete09;

Quando criamos um novo web site na versão 3.5 da plataforma .NET, leia-se VS 2008 ou VWD 2008, já temos incluídos de forma automática os registros necessários ao uso do AJAX no arquivo web.config e os principais controles ASP .NET AJAX estarão na ToolBox na guia AJAX Extensions.

A versão do ASP.NET AJAX da versão 3.5 da plataforma .NET possui um diversas melhorias como o suporte da utilização do UpdatePanel com WebParts, o suporte para WCF baseado em JSON, etc., além das correções de diversos bugs da versão anterior.

Na página Default.aspx , a partir da ToolBox, inclua o componente ScriptManager usado para gerenciar os recursos do AJAX.(A função do Script Manager  é gerenciar a ação de todos os controles AJAX na página.)

Agora vamos criar o banco de dados clicando no menu WebSite -> Add New Item e selecionando o template SQL Server DataBase e informando o nome Pesquisas.mdf;

O arquivo Pesquisas.mdf será criado na pasta App_Data do web site;

Clique duas vezes sobre o banco de dados e o mesmo será aberto na janela DataBase Explorer;

Expanda os objetos do banco de dados e clique com o botão direito sobre o item Tables e selecione Add new Table;

A seguir vamos definir uma tabela chamada Paulista com os campos : id , campeao e ip;

O campo id é uma chave primária do tipo identity(auto-numeração), o campo campeao registra o voto do usuário e o campo ip registra o ip do usuário permitindo que o voto seja feito uma única vez. A seguir o leiaute da tabela :

Devemos agora incluir uma referência ao LINQ to SQL para poder efetuar o mapeamento OR/M;

No menu WebSite clique na opção Add New Item e em Templates selecione LINQ to SQL Classes e informe o nome Paulista.dbml e clique em Add;

Será criado o arquivo Paulista.dbml na pasta App_Code. Selecionando o arquivo Paulista.dbml na janela Solution Explorer iremos ver:

A tabela Paulsita do banco de dados será mapeada como uma classe (campos como propriedades, procedures e funções como métodos)  e você terá no Descritor a classe que representa os objetos do banco de dados; Cada propriedade definida no objeto Paulista esta mapeada para cada coluna da tabela Paulista. (o nome pode ser alterado).

O arquivo Paulista.dbml contém o arquivo XML com informações sobre o leiaute das tabelas que foram mapeadas e também o descritor contendo as classes geradas pelo mapeamento. Após encerrar o mapeamento você já terá acesso aos recursos do LINQ To SQL com direito a intellisense completo das informações referente as tabelas mesmo sem conhecer nada sobre elas.

Se você observar as propriedades do DataContext gerado irá perceber que o nome dado ao contexto é PaulistaDataContext e verá também a conexão com o banco de dados Pesquisas.mdf

O Banco de dados é mapeado em um objeto DataContext permitindo acesso a tabelas de forma transparente sem termos que nos preocupar com conexão. Teremos que apenas usar os recursos do LINQ To SQL.

O DataContext é o responsável por gerar as instruções SQL da sua linguagem de consulta e então mapear as linhas de dados retornadas a partir do seu banco de dados para objetos.

Vamos agora efetuar a vinculação dos objetos com os dados e a interface; a partir da ToolBox, na aba Data, selecione , arraste e solte o componente LinqDataSource para o formulário Default.aspx.

A seguir em LinqDataSource Tasks, marque o item Enable Insert e a seguir clique no link Configure Data Source...;

Na janela Configure Data Source aceite a seleção para PaulistaDataContext e clique em Next>;

A seguir defina todos os campos da tabela Paulista e clique em FInish;

Com isso já podemos usar os recursos do link para incluir dados na tabela Paulista registrando assim o voto dos usuários.

Vamos cuidar da interface...

A página para registrar os votos - Default.aspx

Selecione o arquivo Default.aspx e a partir da ToolBox, aba AJAX Extensions, inclua um componente UpdatePanel no formulário web logo abaixo do componente ScriptManager;

A seguir vamos incluir os componentes : RadioButtonList , Button e Label no interior do UpdatePanel conforme o leiaute abaixo:

RadioButtonList - ID = radVote e Itens conforme figura abaixo:

Button : ID= butVote

O código da página pode ser visto abaixo:  
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" /> 
        <div style="color: #0000FF; font-family: 'Trebuchet MS'; font-size: large">
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
            <br />
                 <b>Qual time vai ser o Campeão Paulista de 2009?</b><br />
                 <asp:RadioButtonList ID="radVote" runat="server">
                 <asp:ListItem>Corinthians</asp:ListItem>
                 <asp:ListItem>Palmeiras</asp:ListItem>
                 <asp:ListItem>Santos</asp:ListItem>
                 <asp:ListItem>São Paulo</asp:ListItem>
                 </asp:RadioButtonList>
                 <asp:Button ID="butVote" runat="server" Text="Vote" onclick="butVote_Click" /><br />
                 <asp:Label ID="lblStatus" runat="server" /><br />
                 <asp:LinqDataSource ID="LinqDataSource1" runat="server" 
                    ContextTypeName="PaulistaDataContext" TableName="Paulistas">
                </asp:LinqDataSource>
            </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>

Vamos agora tratar do código da página Default.aspx. No evento Click do botão - Vote - inclua o código abaixo:

   protected void butVote_Click(object sender, EventArgs e)
    {

        //REMOTE_ADDR nem sempre fornece o ip do usuário
        //mas o endereço ISP por isso primeiro verifique
        //HTTP_X_FORWARDED_FOR para obter o ip real do usuário
        
        string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

        if (ip == null || ip == string.Empty)
            ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
        
        if (ipJaVotou(ip))
        {
            lblStatus.Text = "Você ja votou !!!.";
            Response.Redirect("resultado.aspx?v=ok");
        }
        else
        {
            if (radVote.SelectedItem == null)
            {
                lblStatus.Text = "Informe o seu voto.";
            }
            else
            {
                contaVoto(radVote.SelectedItem.ToString(), ip);
            }
        }
    }

Este código obtém o ip do usuário através de : HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

Para recuperar informações dos usuários que acessam um sistema WEB, usamos a coleção ServerVariables do objeto Request.

A coleção ServerVariables contém todas as informações geradas a partir de uma determinada requisição WEB combinada com variáveis do servidor.

Podemos usar duas varíaveis: HTTP_X_FORWARDED_FOR e REMOTE_ADDR.

  • HTTP_X_FORWARDED_FOR : recupera o IP do HOST caso a conexão à Internet for feita usando um Proxy.
  • REMOTE_ADDR : recupera o IP do HOST  caso a conexão à internet for feita sem usar um Proxy.

A seguir chama a função ipJaVotou(ip) passando o ip do usuário para verificar se o usuário já votou.

Se usuário não votou ainda obtemos o valor do voto e chamamos a rotina contaVoto passando o valor do voto registrado. Se o usuário já votou chamamos a página resultado.aspx passando o parâmetro v=0k que indica que o ele já votou.

O código da função ipJaVotou que verifica se o usuário já votou é dada a seguir:

   protected Boolean ipJaVotou(string enderecoIP)
    {
        //não votou
        Boolean valor = false;
        
        PaulistaDataContext db = new PaulistaDataContext();

        //verifica quantas vezes o valor ip aparece na tabela
        //se for maior que zero significa que o ip ja votou
        int contador = (from c in db.Paulistas where c.ip == enderecoIP select c.ip).Count();

        if (contador > 0)
            //ja votou
            valor = true;

        // true -> ja votou   
        // false -> nao votou
        return valor;
    }

Neste código eu estou criando uma instância do DataContext e em seguida usando uma consulta LINQ com expressão lambda para verificar quantos valores com o ip fornecido já existem gravados. Se o valor for maior que zero temos que este ip já foi registrado, neste caso o retorno será true.

O que são Expressões Lambda ?

As expressões lambda foram incluídas no VS/VB 2008 para dar suporte a consultas LINQ. As cláusulas Where são assim compiladas como expressões lambdas e chamadas em itens aplicáveis do seu dataset. Podem ser consideradas uma forma de delegate que pode passar ou retornar outra função.

Nota: Delegates permitem que uma classe use métodos de outra classe. Para saber mais sobre delegates leia o meu artigo: Usando Delegates

No LINQ as expressões lambda são usadas para realizar ações sobre listas de objetos e com extensões de métodos.

Uma expressão lambda é então uma função sem nome que calcula e retorna um valor único e podem ser usadas em qualquer lugar que um tipo delegate for válido.

fonte:  ASP .NET - Usando LINQ - Expressões Lambada

Nota: Em produção a rotina para verificação de ip teria que ser mais bem elaborada.

O código da função contaVoto usada para registrar o voto é o seguinte:

 protected void contaVoto(string voto,string ip)
    {
        try
        {
            PaulistaDataContext context = new PaulistaDataContext();

            Paulista pt = new Paulista();
            pt.campeao = voto;
            pt.ip = ip;
            context.Paulistas.InsertOnSubmit(pt);
            context.SubmitChanges();
            lblStatus.Text = "Obrigado por participar.";

            Response.Redirect("resultado.aspx");
            //lerDados();
        }
        catch
        {
            lblStatus.Text = "Erro ao processar o seu voto, tente novamente.";
        }
    }

Neste código criamos uma instância do datacontext e da classe Paulista e a seguir atribuímos o voto e o ip aos campos campeao e ip do objeto pt que é uma instância da classe Paulista.

Em seguida usamos o método InsertOnSubmit() do DataContext e para salvar os dados o método SubmitChanges().

- InsertOnSubmit() - inclui um novo objeto a coleção de Votos incluindo uma entidade em um estado pendente de inclusão para esta a tabela Paulista;

- SubmitChanges() - Este método efetiva a inclusão atual feita pelo InsertOnSubmit() na tabela do banco de dados. Deve ser chamado após o método InsertOnSubmit();

Nota: Quando SubmitChanges() é invocado o LINQ To SQL automaticamente gera e executa comandos SQL a fim de transmitir as alterações de volta ao banco de dados. Você pode no entanto sobrescrever este comportamento chamando uma stored procedure.

Notou que não nos preocupamos com string de conexão, comandos SQL, criação de objetos para incluir o registro na tabela, etc.

Exibindo o resultado

A página resultado.aspx tem duas funções: exibir uma mensagem ao usuário e exibir o resultado da votação.

Ela é composta pelos controles Label, Button e Literal conforme o leiaute abaixo:

Vejamos a seguir o código desta página:

1- No evento Load da página temos:

   protected void Page_Load(object sender, EventArgs e)
    {
        if ( Request.QueryString["v"].Equals("ok"))
        {
            Label1.ForeColor = System.Drawing.Color.Red;
            Label1.Text = " VOCÊ JÁ VOTOU ";
        }
        else
        {
            Label1.ForeColor = System.Drawing.Color.Blue;
            Label1.Text = " OBRIGADO POR PARTICIPAR DA NOSSA CONSULTA ";
        }
    }

O código verifica se o usuário já votou exibindo uma mensagem apropriada.

No evento Click do botão Exibir Resultado apenas chamamos a rotina lerDados();

protected void btnResultado_Click(object sender, EventArgs e)
{
     lerDados();
}

A rotina lerDados() mostrada a seguir usa uma consulta LINQ para obter os votos para cada opção e em seguida efetua os cálculos para exibir o resultado

    protected void lerDados()
    {
        PaulistaDataContext context = new PaulistaDataContext();

        Paulista pt = new Paulista();

        var votos = from campeao in context.Paulistas select campeao;

        int gamba = 0;
        int porco = 0;
        int peixe = 0;
        int santo = 0;

        foreach (var voto in votos)
        {
            if (voto.campeao == "Corinthians")
                gamba++;
            else if (voto.campeao == "Palmeiras")
                porco++;
            else if (voto.campeao == "Santos")
                peixe++;
            else if (voto.campeao == "São Paulo")
                santo++;
        }

        double Total = 0;
        Total = gamba + porco + peixe + santo;
        double gamba_percentual = 0;
        double porco_percentual = 0;
        double peixe_percentual = 0;
        double santo_percentual = 0;

        gamba_percentual = (gamba / Total) * 100;
        porco_percentual = (porco / Total) * 100;
        peixe_percentual = (peixe / Total) * 100;
        santo_percentual = (santo / Total) * 100;

        litResultado.Visible = true;
        litResultado.Text = "Corinthians: " + gamba + " votos (" + gamba_percentual + "%).<br />";
        litResultado.Text = litResultado.Text + "Palmeiras: " + porco + " votos (" + porco_percentual + "%).<br />";
        litResultado.Text = litResultado.Text + "Santos: " + peixe + " votos (" + peixe_percentual + "%).<br />";
        litResultado.Text = litResultado.Text + "São Paulo: " + santo + " votos (" + santo_percentual + "%).<br /><br />";
    }

Executando o projeto teremos a página Default.aspx sendo apresentada;

Após selecionar uma opção e clicar no botão Vote teremos a página Resultado.aspx exibindo a mensagem e o resultado, se o usuário clicar no botão Exibir Resultado;

Para perceber a utilização AJAX o correto seria colocar o botão de resultado na mesma página de votação, esses detalhes e também o tratamento de erros eu deixo para você se exercitar.

Este é um exemplo básico de utilização do LINQ to SQL em combinação com AJAX em uma aplicação ASP .NET útil que pode ser incrementada e usada em seus projetos.

Pegue o projeto completo aqui: enquete09.zip

Eu sei é apenas ASP .NET mas eu gosto...

Referências:


José Carlos Macoratti