Skip Navigation Links



Translate this page now :



»Programação
»Programação.NET
»Banco de Dados
»Webdesign
»Office
» Certificações Microsoft 4
»Treinamentos4
»Programação 4
»Webdesign«
»Office & User Tips«
»Grupos de Usuários
»Células Acadêmicas«
intcontpiada : 118
Os 3 Porquinhos
Você já está cadastrado e participa do grupo de usuários de sua cidade ? Se não, comente o porque.
 
 
Faça um pequeno teste com 10 questões de VB
.:.
Teste seus conhecimentos em Visual Basic, SQL Server e ASP 3.0 com nossas provas on-line
.:.
Aprimore seus conhecimentos em programação com nosso treinamento on-line de lógica de programação
.:.
Veja nosso calendário de treinamentos
Gostou da Página?
Então

para um amigo!

Pesquisa personalizada
Pesquisar Dicas:

 







Evitando a re-execução de tarefas devido ao refresh de páginas

A versatilidade da web acaba causando alguns problemas para aplicações criadas para ambiente web. Um dos problemas mais conhecidos é a simplicidade com a qual o usuário, apenas pressionando F5, consegue refazer operações que não poderiam ser feitas novamente, tal como deleção e inserção de dados.

Com o .NET temos novas e bem interessantes formas de resolver este problema da re-execução de páginas. Podemos criar uma classe e, utilizando herança, fazer com que seja utilizada como base para todas as páginas web.

Mas como reconhecer se a requisição é uma requisição original ou um refresh de página ?

Para isso podemos utilizar um algorítimo simples : Para cada requisição realizada por um usuário damos um ticket da requisição, um valor numérico incremental. Esse valor fica oculto na página, em um campo hidden e também guardado em sessão.

A cada nova requisição comparamos o valor do ticket com o valor contido em sessão. Se em algum momento o valor do ticket for menor que o valor contido em sessão então é porque ocorreu um refresh da página. Podemos então sinalizar em uma propriedade da página que a chamada trata-se de um refresh, permitindo a nosso código controlar a realização de operações críticas.

Vamos então criar uma classLibrary para realizar estas tarefas. Vou chamar esta classLibrary de libNotRefresh. Precisaremos adicionar nesta classLibrary uma referência para System.Web, já que isso não é default.

Vamos inicialmente criar uma classe para encapsular o acesso aos valores do ticket em sessão, vou chama-la de clsTicket. Veja como fica o código, bem simples :

Imports System.Web.HttpContext

Public Class clsTicket

 

    Public Shared Function NextTicket() As Integer

        If IsNothing(Current.Session("LastTicket")) Then

            Current.Session("LastTicket") = 1

        Else

            Current.Session("LastTicket") += 1

        End If

        Return (Current.Session("LastTicket"))

    End Function

 

    Public Shared Function LastTicket() As Integer

        If Not IsNothing(Current.Session("LastTicket")) Then

            Return (Current.Session("LastTicket"))

        Else

            Current.Session("LastTicket") = 0

            Return (0)

        End If

    End Function

 

End Class

 

Nesta classe podemos observar uma característica interessante que nem todos conhecem : É possível acessar os objetos do ASP.NET a partir de um componente.

Toda requisição web tem um contexto, o contexto http da requisição. Então utilizando System.Web.HttpContext.Current temos acesso aos objetos do ASP.NET com os valores do contexto atual da requisição, mesmo dentro de um componente.

Temos nesta classe dois métodos : LastTicket para fornecer o valor do último ticket que foi criado e NextTicket, para criar um novo ticket e fornecer seu valor. Em ambos os métodos foi tomado o cuidado de verificar se o valor em sessão está realmente preenchido. Ambos estão também definidos como shared, para simplificar o acesso a eles.

O próximo passo é garantirmos que todas as páginas gerem o ticket na forma de um campo hidden. Para fazermos isso podemos criar uma classe filha de System.web.Ui.Page e fazer com que todas as páginas de nosso site herdem as características desta classe.

Veja como fica :

Public Class paginaBase

    Inherits System.Web.UI.Page

 

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)

        Me.RegisterHiddenField("__Ticket", clsTicket.NextTicket)

        MyBase.OnPreRender(e)

    End Sub

 

End Class

É importante lembrarmos neste ponto de um padrão utilizado pela Microsoft nas classes do framework .NET . Para cada evento existente nestas classe existe um método equivalente com o mesmo nome do evento acrescido de "On" e definido como protected.

Este método é disparado dentro das classes no momento imediatamente anterior a ocorrencia do evento e é este método que é o responsável por disparar o evento.

Isso ocorre para que ao criarmos uma herança da classe possamos fazer uso do evento substituindo este método, ao invés de utilizar o evento diretamente. Isso além de simplificar o uso do evento mantém o evento disponível para as aplicações que serão simples usuárias da classe.

Assim sendo, em nossa classe paginaBase fiz overrides do método onPreRender, que ocorre imediatamente antes do evento PreRender. É o momento ideal de incluir algo na resposta, no caso o campo hidden __Ticket.

Para incluir este campo hidden utilizei o método RegisterHiddenField. Este método simplifica muito o trabalho pois já verifica se já existe ou não um campo hidden com este nome. Caso já exista apenas troca o valor, ao invés de duplicar o campo. Outra opção, até mais discreta, seria fazer uso do viewState.

Agora que fizemos o código para gerar o campo hidden devemos preparar o código para recebe-lo de volta, testar o ticket e configurar uma nova propriedade na página, isRefresh, para determinar se está ou não ocorrendo um refresh nesta página.

Para fazer isso vamos fazer um overrides no método onInit, responsável pelo evento Init do objeto Page. Veja como fica :

Public Class paginaBase

    Inherits System.Web.UI.Page

    Dim _isRefresh As Boolean = False

 

    Public ReadOnly Property isRefresh() As Boolean

        Get

            Return (_isRefresh)

        End Get

    End Property

 

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)

        Me.RegisterHiddenField("__Ticket", clsTicket.NextTicket)

        MyBase.OnPreRender(e)

    End Sub

 

    Protected Overrides Sub OnInit(ByVal e As System.EventArgs)

        Dim TickAtual, UltimoTick As Integer

        _isRefresh = False

 

        UltimoTick = clsTicket.LastTicket

 

        If IsNothing(Request.Form("__Ticket")) Then

            TickAtual = 0

        Else

            TickAtual = Request.Form("__Ticket")

        End If

 

        If TickAtual < UltimoTick Then

            _isRefresh = True

        End If

        MyBase.OnInit(e)

    End Sub

End Class

No método onInit comparamos o valor existente em sessão com o valor recebido via campo hidden. Se o valor no campo hidden for menor que o contido em sessão então ocorreu um refresh.

Vamos então fazer uma página para testar isso. Vamos fazer uma página com uma caixa de texto, um botão e uma listbox. Veja como fica inicialmente o código :

Public Class WebForm1

    Inherits libNotRefresh.paginaBase

 

     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

 

        ListBox1.Items.Add(TextBox1.Text)

    End Sub

 

 

End Class

Faça um teste inicial com relação ao refresh. Insira algumas palavras, tal como "teste1", "teste2" e "teste3" na listbox e por fim faça refresh várias vezes. Verá que por mais que tente a última palavra não será duplicada na listBox.

Mas e o problema do refresh ? Por que neste exemplo ele não causa a duplicação ?

Quando vemos a palavra "teste3" (por exemplo) dentro da listbox, ao darmos refresh a transmissão para o servidor será do conteúdo anterior, "teste1" e "teste2", sem o teste3. Quando o código é re-executado a palavra "teste3" é re-inserida.

O mesmo acontece com labels, isso porque apesar dos labels não serem transmitidos para o servidor em um POST, eles guardam seu conteúdo no viewState.

Assim sendo com relação a interface visual o problema do refresh tem impacto mínimo. Ele apenas se torna mais grave em tarefas que vão além da interface, como por exemplo a inserção de um registro. Veja o código a seguir :

Public Class WebForm1

    Inherits libNotRefresh.paginaBase

 

     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

 

        ListBox1.Items.Add(TextBox1.Text)

        cmd.Parameters("texto").Value = TextBox1.Text

        CN.Open()

        cmd.ExecuteNonQuery()

        CN.Close()

    End Sub

 

 

End Class

Criei um command e uma conexão para gravar o conteúdo da textbox em uma tabela de um banco de dados. Se após isso você testar novamente o refresh, observará na tabela do banco que o texto será duplicado de acordo com o número de vezes que você fizer refresh.

Para fazermos com que isso não aconteça agora é bem simples : Basta alterarmos a herança, para que nossa página herde da classe paginaBase e inserirmos no click do botão um if para testar a nova propriedade isRefresh.

Veja como fica :

Public Class WebForm1

    Inherits libNotRefresh.paginaBase

 

     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

 

        ListBox1.Items.Add(TextBox1.Text)

        If Not isRefresh Then

            cmd.Parameters("texto").Value = TextBox1.Text

            CN.Open()

            cmd.ExecuteNonQuery()

            CN.Close()

        End If

    End Sub

 

 

End Class

Pronto. Testando novamente você observará que a inserção no banco não mais será duplicada. Basta fazermos o mesmo em todas as nossas páginas, trocarmos a herança e testarmos a propriedade isRefresh, para desta forma termos o controle da realização de refreshs nas páginas por parte dos usuários.

Dennes Torres
MCAD,MCSD,MCSE,MCDBA





Envie seus comentários sobre este artigo

Nome :

E-mail :

Comentários :


Avise-me quando houverem novos comentários nesta página

Veja abaixo os comentários já enviados :

Nome : Leonardo E-Mail : leonardo_bisogno@hotmail.com
Simplesmente muito bom!
Eu e meus colegas de trabalho pensávamos que não havia como resolver esse problema do refresh. Já até estávamos conformados com isso.

A dica é ótima como disse, mas o que me chamou mais a atenção foi o total domínio sobre como utilizar os recursos oferecidos pelo framework .net. Isso me fez pensar no longo caminho que ainda tenho pela frente.

Muito obrigado,
Leonardo T. Neves Bisogno
Nome : Carlos E-Mail : crczzampronha@ibest.com.br
muito interessante
Nome : Anderson E-Mail : anderson@onlinegyn.com.br
Deveríamos usar este procedimento descrito acima?
Nome : Junior E-Mail : junior@usp.br
Oi Dennes e como eu faço para além disso usar aquele recurso que eu vejo em alguns sites de desabilitar o botão...
Nome : Marcos E-Mail : mrgouvea@ibest.com.br
parabéns, muito bom!!
Nome : Felipe E-Mail : fboriani@gmail.com
Olá Dennes, ótimo artigo, resolveu muitos problemas aqui no trabalho, porém gostaria de saber como evitar este refresh quando a página faz alguns postback dentro de um updatepanel, pois o evento PreRender não executa...

Obrigado
Nome : Oraculum E-Mail : damon.abdiel@gmail.com
Felipe não sei se o que fiz vai adiantar pra você também:

http : // oraculum.blog.br/blogoraculum/index.php/2009/09/23/evitando-refresh-com-update-panel/

Fiz meio na gambiarra depois eu melhoro quanto tiver um pouco mais de tempo.

O meu código está em c# caso alguém precise :)
Nome : Flávio E-Mail : flavio.tux@gmail.com
Realmente funcionou.
Obrigado.
Nome : E-Mail :
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
-1'
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1tSuJLQs E-Mail : f7khsr49i@gmail.com
I saw the message that inaicdted to turn off the custom errors. They were allready turned off, but I didn't realise they had to be turned off in 2 web.config files. So thanks for sharing the link.After adjusting the second web.config I received the real errormessage, indicating that my custom peoplepickerdialog wasn't a safe control. So I added it to safecontrols in the web.config. Now I'm able to filter on department. But there's lots of work to be done.First: The custom PeopleEditor has another behavior as the standard one. The check user isn't working as it supose to. Replacing only the pickerdialog while keeping the standard PeopleEditor seems to be unposible. So I will have to figure out how to progam my custom PeopleEditor with the same behavior as the standard one.Second: The PickerDialog resovles all users imported by the user profile service. That can take a while. So there is some sort of limitaion needed.You have put on the right track. So I like to thank you for sharing this valuable information.
Nome : cheap oakley sunglasses E-Mail : blenyfwxpwq@gmail.com
I want to express some thanks to the writer for rescuing me from this particular trouble. After surfing through the online world and obtaining basics which are not pleasant, I assumed my entire life was done. Living devoid of the solutions to the problems you've resolved by way of this short article is a serious case, and the ones which may have badly damaged my entire career if I hadn't encountered your web blog. Your own personal know-how and kindness in playing with a lot of stuff was very helpful. I don't know what I would have done if I hadn't discovered such a thing like this. I can at this time look ahead to my future. Thanks a lot so much for the skilled and effective help. I won't hesitate to propose your web page to any individual who needs to have counselling on this subject.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Conheça mais sobre o nosso site :

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



Quer saber mais?
Faça um curso na Búfalo Informática, Treinamento e Consultoria e
Prepare-se para o Mercado!
Veja o que a Búfalo tem para você.

� Búfalo Informática, Treinamento e Consultoria - Rua Álvaro Alvim, 37 Sala 920 - Cinelândia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail:
contato@bufaloinfo.com.br