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
Guilherme Tell
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:

 







Custom Paging com ObjectDataSource e GridView

A datagridview do framework 1.1 permitia o custom paging, conforme já publicamos artigo anteriormente. A partir do framework 2.0, porém, passou a ser necessário fazer uso do ObjectDataSource para a realização de paginação customizada, ou seja, passou a ser necessário ter um objeto que forneça para a gridview as informações de cada página de dados.

As vantagens do custom paging é permitir que a cada ida ao servidor apenas os dados que realmente serão exibidos possam ser recuperados. A outra opção seria trazer os dados apenas uma vez, guarda-los em sessão ou cache e utilizar a gridview para exibi-los. Sobre esta opção já escrevemos anteriormente.

Então vamos fazer um exemplo de custom paging com a gridview utilizando objectDataSources

Criando a Camada de Dados/Negócios

1) Crie uma class library chamada libdados

2) Delete a classe existente e insira um novo dataset chamado dsClientes

3) Pela toolbox adicione um novo tableAdapter e, utilizando o wizard, configure-o para trazer os dados da tabela customers (customerid,companyname,contactname,country)

4) Monte métodos personalizados para cada tarefa (ApagarRegistro, InserirRegistro,AtualizarRegistro)

Desta forma estamos seguindo a recomendação já explicada no artigo em http://www.bufaloinfo.com.br/artigos/artigo26062006.asp

 

Criando a Camada de Interface

1) Utilize o File-Add->New WebSite para adicionar um novo projeto na mesma solução

(Cuidado para não confundir com File->New WebSite!)

2) Adicione uma referência do web site para o projeto class library

3) Adicione um objectDataSource na página default.aspx e ligue-o com o customersTableAdapter

4) Configure cada ação (Select/Insert/Update/Delete) para seu devido método

5) Adicione uma gridview e ligue-a com o ObjectDataSource

6) Habilite paginação, ordenação, deleção e edição, mova o commandField para a última coluna e faça um auto-format

Apenas detalhes cosméticos

7) Teste a aplicação

 

Em que Ponto Estamos

Até este momento ainda não temos o custom paging, apenas uma gridview comum, com a única diferença de estar vinculada a um ObjectDataSource

Para termos a paginação personalizada precisaremos organizar nossa classe CustomersTableAdapter - para atender as seguintes propriedades :

SelectCountMethod : Precisaremos indicar um método que retorne o count de registros do banco de dados

MaximumRowsParameterName : Este parâmetro será utilizado para indicar o total máximo de linhas a ser devolvido em cada página

startRowIndexParameterName : Este parâmetro indica a linha inicial que deve ser devolvida na paginação

Desta forma precisaremos de um novo método que faça o trabalho do SelectCount e ajustar o método de "select" para aceitar os dois novos parâmetros e fazer a paginação

Paginação na Camada de Negócios

1) Adicione uma nova query (chame-a de TotalRegistros) no tableAdapter utilizando o tipo "Select que retorna um único valor" e a seguinte query SQL :

Select count(*) from customers

2) Altere a query padrão (fill/getdata) para que fique da seguinte forma :

select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by customerid)
as rownum
from customers) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows

 

Observe que utilizei na instrução SQL duas variáveis : @startrowindex e @maximumrows. Essas variáveis tem exatamente o nome dos parâmetros especificados nas propriedades MaximumRowsParameterName e StartRowIndexParameterName do ObjectDataSource. Poderia ter personalizado o nome se desejasse, o que importa é que o nome dos dois parâmetros utilizados na instrução SQL seja informado ao ObjectDataSource através das propriedades MaximumRowsParameterName e startRowIndexParameterName.

Por ter utilizado estes 2 parâmetros, eles se tornarão parâmetros dos métodos Fill e getData, sendo passados pelo ObjectDataSource.

Outra questão importante de ser observada é que fiz uso do ROW_NUMBER, função do SQL que apenas surgiu no SQL Server 2005, por isso para versões anteriores do SQL ou outros servidores de dados a instrução SQL precisaria ser adaptada.

Além disso, existe uma pequena diferença de numeração entre a numeração feita pelo ObjectDataSource e a numeração feita pelo SQL Server : o ObjectDataSource inicia a numeração em 0, o SQL Server inicia a numeração em 1. Por isso o startrowindex está sendo incrementado sempre com 1, para corrigir essa diferença.

O tableAdapter, porém, não suporta o uso de Row_Number e Over, por isso acaba não identificando os parâmetros da query. Precisamos ajustar os parâmetros de forma "manual".

3) Selecione o Fill/GetData e abra a janela de propriedades.

4) Abra a janela Parameters Collection clicando nos "..." da propriedade parameters.

5) Clicando no Add, adicione 2 parâmetros

6) Altere a propriedade DBType de ambos para Int32

7) Altere o nome de ambos para startRowIndex e maximumRows respectivamente e feche a janela "Parameters Collection"

Configurando a página para o custom paging

1) Faça um rebuild na solução (isso faz com que a dll do projeto class library seja atualizada na pasta bin do site)

2) Abra novamente a página default.aspx

3) No ObjectDataSource, altere a propriedade EnablePaging para true

Importante

Normalmente, ao alterarmos uma característica no objeto de negócio, teríamos que refazer o Configure Data Source, afinal o ObjectDataSource não sabe que o método de Select que escolhemos - GetData - tem 2 parâmetros.

Porém existe um pequeno truque nisso : Ao fazermos o EnablePaging ser true, o ObjectDataSource já irá considerar estes 2 parâmetros adicionais automaticamente. Então o fato é que não apenas não precisamos mas não podemos re-fazer o "configure data source".

Por que não podemos ?

Porque o método de totalização de registros - TotalRegistros - precisa ter sempre os mesmos parâmetros que o método de busca de registros - GetData - exceto por estes 2 parâmetros de controle de paginação. Se deixarmos o ObjectDataSource configurar estes 2 parâmetros no método GetData, ele nos forçará a fazer com que o método TotalRegistros tenha também estes 2 parâmetros sem necessidade - o que se torna uma gambiarra na camada de negócios e por incrivel que pareça - funciona - mas ainda assim é gambiarra.

4) Preencha a propriedade SelectCountMethod do ObjectDataSource com o nome do método de totalização - TotalRegistros

5) Faça um "view in browser".

Tudo funcionará normalmente, a diferença é que a partir de agora está sendo realizado o custom paging, ou seja, apenas 10 registros por vez são trazidos da base de dados.

Pode-se observar através do SQL Server Profiler que a cada paginação realizada é feito um count dos registros e nosso select é executado trazendo os registros de apenas uma página e não a tabela inteira do banco.

Otimizando a totalização

O método TotalRegistros poderia ser otimizado, caso o total de registros não varie com tanta frequencia. O total de registros poderia ser armazenado em cache/session evitando um count frequente na base de dados. A questão é apenas onde fazer. Se for utilizada a técnica de partial class que apresentamos em http://www.bufaloinfo.com.br/artigos/artigo26062006.asp estaremos tornando nossa camada de negócios específica para web.

A outra opção seria criar uma classe na interface que tenha os mesmos métodos que o tableAdapter, mas aplique o cache no TotalRegistros. Fazendo manualmente teriamos que escrever muito, porém podemos utilizar alguns truques com herança.

1) Adicione uma nova classe no site chamada CustomersTableAdapter

2) Ao ser perguntado sobre inseri-la na pasta App_Code, confirme.

3) Apague o construtor criado automaticamente

4) Faça a classe herdar de LibDados.dsClientesTableAdapters.CustomersTableAdapter

public class CustomersTableAdapter :
LibDados.dsClientesTableAdapters.CustomersTableAdapter
{

}

5) Substitua o método TotalRegistros da seguinte forma :

public Int32 TotalizarRegistros()
{
if (HttpContext.Current.Cache["TotalRegistros"]
== null)
{
HttpContext.Current.Cache["TotalRegistros"]=
base.TotalRegistros();
}

return (Int32)HttpContext.Current.Cache["TotalRegistros"];
}


Observe o seguinte :

A) Se o total não estiver em cache, chamamos o método da classe pai, que fará a consulta a banco e guardará no cache
B) É feito o return do total a partir do cache

6) Volte para a página default.aspx

7) Utilizando a smartTag do ObjectDataSource, entre em "Configure Data Source"

8) Na tela "Choose a Bussiness Object" duas opções passaram a aparecer na dropDownList com o nome do objeto. Escolha o CustomerTableAdapter que aparece sem namespace, é a classe que encontra-se em seu próprio site.

9) No passo seguinte, o método para "Select" aparece vazio, o Wizard forçará você a selecionar o getData com 2 parâmetros. Faça isso.

10) Não preencha nada na configuração "Define Parameters" e finalize o wizard.

Conforme comentamos anteriormente, o método de Select não pode conter os parâmetros de paginação (startRowIndex e maximumRows), mas ao sermos forçados a executar o "Configure Data Source" novamente, estes parâmetros foram incluidos. Precisamos corrigir isso.

11) Nas propriedades do ObjectDataSource, vá em SelectParameters e abra a janela "Parameters Collection"

12) Remova os 2 parâmetros que aparecem na janela "Parameters Collection"

13) Altere a propriedade SelectCountMethod do ObjectDataSource para "TotalizarRegistro"

14) Salve a página e faça um "View in Browser".

15) Pagine várias vezes a grid

Poderá observar pelo SQL Server Profiler que o Count é executado apenas uma vez, após isso o resultado encontra-se em cache. Uma tabela de clientes não é o melhor exemplo para usar este tipo de cache, pois novos clientes podem surgir a qualquer momento. Porém em uma tabela de produtos, por exemplo, o surgimento de novos produtos na empresa é eventual, justificando o cache.

Para que o cache possa ser automaticamente invalidado quando um novo item for incluido na tabela, podemos utilizar os recursos demonstrados neste artigo ....

Filtrando Dados

Para analisarmos melhor o custom paging precisamos observar como ele se comporta se aumentarmos a complexidade de nossa aplicação. Em nosso exemplo, vamos supor que desejamos fazer uma filtragem dos clientes por país.

Para manter a consistência do exemplo, vamos continuar a fazer todo o acesso a dados em camadas.

Precisaremos realizar as seguintes etapas para fazer a filtragem :

A) Montar um TableAdapter para ler uma lista de paises para o usuário escolher e filtrar
B) Montar a interface em que será feita a filtragem
C) Alterar os métodos GetData e TotalRegistros para levarem em consideração a filtragem
D) Alterar o ObjectDataSource1 para se ligar aos novos métodos, passar o parâmetro e assim fazer a filtragem

Criando o novo tableAdapter

1) No dsClientes da libDados, arraste um novo tableAdapter a partir da ToolBox.

2) Configure o tableAdapter com a seguinte query :

Select Distinct Country from Customers

3) Em "Advanced Options" desmarque a opção "Generate Insert, Update and Delete"

4) Siga adiante e finalize o wizard.

5) Altere o nome da nova DataTable para Paises

6) Altere o nome do novo TableAdapter para PaisesTableAdapter

Montando a interface de filtragem

1) Faça um rebuild da solução

2) Abra novamente a página default.aspx

3) Adicione um novo ObjectDataSource no topo da página

4) Utilizer o "Configure Data Source" e selecione o PaisesTableAdapter

5) Ainda no wizard, deixe o getData (que já aparecerá por padrão) como método de Select. Os demais não serão configurados.

6) Adicione uma DropDownList na página e dê o nome de ddlPaises

7) Pela smartTag, habilite o AutoPostBack da DropDownList.

8) Ainda pela smartTag, entre em Choose Data Source

9) Selecione o ObjectDataSource2 como origem de dados e o campo Country aparecerá automaticamente nos 2 espaços abaixo.

10) Pela smartTag, entre em Edit Items

11) Adicione um novo ListItem clicando no botão Add

12) Altere o text para "Todos", o Value se tornará Todos automaticamente.

13) Altere a propriedade "Selected" para true e dê Ok nesta tela

14) Altere a propriedade AppendDataBoundItems da DropDownList para true.

Do contrário o conteúdo trazido do banco apagará o item "Todos" na dropdowlist.

Alterando os métodos originais

1) Altere o Fill/GetData do CustomersTableAdapter (no dataset) para que a query fique da seguinte forma :

select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by customerid)
as rownum
from customers
where (country=@country) or (@country='Todos')
) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows

Observe que já estou deixando uma brecha para que exista um item "Todos", sem a realização da filtragem.

Da mesma forma que antes, o parâmetro não será reconhecido.

2) Altere a coleção de parâmetros do Fill/GetData (abrindo a propriedade parameters) e acrescente o parâmetro country, com DbType string.

3) Faça com que o parâmetro country seja o primeiro parâmetro da lista de parâmetros e feche a janela "Parameters Collection".

4) Altere a query do método TotalRegistros para que fique da seguinte forma :

SELECT COUNT(*) FROM Customers
where (country=@country) or (@country='Todos')

A totalização precisa sempre seguir sempre a mesma filtragem da query principal.

Fazer a filtragem na interface

1) Abra a classe CustomersTableAdapter do projeto web

2) Altere o método TotalizarRegistros para que fique da seguinte forma :

public Int32 TotalizarRegistros(string Country)
{
if (HttpContext.Current.Cache["TotalRegistros" + Country]
== null)
{
HttpContext.Current.Cache["TotalRegistros" + Country]=
base.TotalRegistros(Country);
}

return (Int32)HttpContext.Current.Cache["TotalRegistros" + Country];
}

Precisamos agora manter um valor diferente no cache para cada filtragem realizada, por isso a mudança. Repare no truque de montar dinamicamente o nome da variável que será mantida em cache.

3) Faça um rebuild da solução

4) Abra a página default.aspx

5) Utilizando a smartTag do ObjectDataSource1, entre em "Configure Data Source"

6) Avance no wizard até a janela "Define Data Methods"

O método de select aparece em branco, porque seus parâmetros foram alterados

7) Escolha como método de Select o getData que aparece na dropdown, agora com 3 parâmetros.

8) No passo seguinte, "Define Parameters", com o parâmetro "Country" selecionado, preencha o Parameter Source como "Control" (a informação vem de outro controle na tela).

9) Em ControID selecione a ddlPaises

10) Finalize o wizard (botão "Finish")

11) Nas propriedades do ObjectDataSource1, abra a janela "Parameters Collection" utilizando a propriedade SelectParameters

12) Como da última vez, apague os parâmetros referentes a paginação - startRowIndex e maximumRows.

13) Teste a aplicação

Tudo estará funcionando, desta vez com a filtragem e juntando a isso a funcionalidade de custom paging.

 

Ordenação dos Dados

A ordenação dos dados é algo que, em nosso projeto até o momento, encontra-se desastrosa : Pode experimentar, pagine, ordene, pagine novamente, verá que não está funcionando.

Com Custom Paging, a ordenação precisa ser realizada direto na base de dados e não pelo gridview. Para isso precisaremos novamente alterar nosso método GetData no CustomersTableAdapter e utilizar alguma programação na página default.aspx

Alterando o TableAdapter

1) No banco Northwind, crie a seguinte stored procedure :

Create Procedure lerClientes
@country varchar(50), @ordenacao int, @startrowindex int, @maximumrows int
AS

if @ordenacao=1
Begin
select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by customerid)
as rownum
from customers
where (country=@country) or (@country='Todos')
) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows
End

 

if @ordenacao=2
Begin
select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by companyname)
as rownum
from customers
where (country=@country) or (@country='Todos')
) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows
End

if @ordenacao=3
Begin
select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by contactname)
as rownum
from customers
where (country=@country) or (@country='Todos')
) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows
End

if @ordenacao=4
begin
select customerid, companyname, contactname, country
from
(select customerid,companyname, contactname,country,
ROW_NUMBER() over(order by country)
as rownum
from customers
where (country=@country) or (@country='Todos')
) a
where a.rownum >=@startrowindex +1
and a.rownum<(@startrowindex+1)+ @maximumrows
end

Infelizmente não há solução simples para isso. O order by da clausula OVER não aceita indice numérico para indicar o campo (o order by típico aceita), desta forma não há como parametrizarmos, a solução é fazermos uma série de IFs verificando a ordenação desejada e fazendo a query conforme a ordenação solicitada.

2) Entre na configuração do Fill/GetData

3) Clique no botão "Previous"

4) Selecione a opção "Use existing stored procedure" (utilizar uma stored procedure existente) e siga adiante.

5) Na etapa seguinte, selecione a stored procedure "LerClientes" como procedure de Select.

6) Siga adiante e finalize o wizard

7) Verifique os campos da datatable, se continuam corretos. Eventualmente pode ocorrer duplicação, neste caso basta deletar o excesso.

Preparando a Interface para a Ordenação

1) Faça um rebuild na solução

2) Abra a página default.aspx

3) Utilizando a smartTag do ObjectDataSource1, entre em "Configure Data Source"

4) Avance no wizard até a janela "Define Data Methods"

O método de select aparece em branco, porque seus parâmetros foram alterados

5) Escolha como método de Select o getData que aparece na dropdown, agora com 4 parâmetros.

6) No passo seguinte, "Define Parameters", com o parâmetro "Country" selecionado, preencha o Parameter Source como "Control" (a informação vem de outro controle na tela).

7) Em ControID selecione a ddlPaises

8) Finalize o wizard (botão "Finish")

Não configuramos o parâmetro "ordenacao", seu preenchimento será por código.

9) Caso seja peguntado se deseja fazer um refresh na gridview, sempre responda que não.

10) Nas propriedades do ObjectDataSource, vá em SelectParameters e abra a janela "Parameters Collection"

11) Remova os 2 parâmetros de paginação (startRowIndex e maximumRows) que aparecem na janela "Parameters Collection"

Preenchendo o parâmetro de ordenação

Haverá uma ordenação default e eventualmente o usuário clicará no título de uma coluna para realizar a ordenação pelo campo que deseja.

Ocorre que muitos outros postbacks serão feitos que não envolvem ordenação : Filtragem e paginação, por exemplo. Mesmo nestes postbacks o parâmetro indicando por qual campo a ordenação está sendo feita precisará ser passado para o método GetData.

Desta forma, vamos guardar no ViewState esta informação, o campo pelo qual a ordenação está sendo realizada, assim poderemos passar essa informação em todas as chamadas do método GetData()

1) Crie a seguinte propriedade :

Public Property Ordenacao() As Integer
Get
If IsNothing(ViewState("ordenacao")) Then
ViewState("ordenacao") = 1
End If
Return ViewState("ordenacao")
End Get
Set(ByVal value As Integer)
ViewState("ordenacao") = value
End Set
End Property

A forma mais organizada de utilizar o ViewState é através de uma propriedade, assim sendo todo o resto da página poderá fazer referência apenas a propriedade.

Observe que caso a variável no viewstate ainda não exista, atribuimos um valor a ela, no caso 1. Estamos assim criando um default para a ordenação, customerID, o primeiro campo do select.

2) Selecione o ObjectDataSource no design da página default.aspx

3) Usando a janela de propriedades, clique no raio dos eventos e dê um duplo clique no evento Selecting

4) No evento Selecting, programe o seguinte :

Protected Sub ObjectDataSource1_Selecting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ObjectDataSourceSelectingEventArgs) Handles ObjectDataSource1.Selecting
e.InputParameters("ordenacao") = Ordenacao
End Sub

Com isso resolvemos os seguintes problemas :

A) Temos um default para a ordenação
B) Guardamos a ordenação através dos postbacks
C) Passamos a ordenação como parâmetro sempre que necessário

 

5) Teste a aplicação : um erro ocorrerá.

Como adicionamos o parâmetro "ordenacao" no GetData do objectDataSource, ele está exigindo que o TotalRegistros receba o mesmo parâmetro.

6) Abra a classe CustomersTableAdapter que encontra-se no site

7) Altere a assinatura do método TotalizarRegistros para que fique da seguinte forma :

public Int32 TotalizarRegistros(string country,int ordenacao)

Este parâmetro de fato não será utilizado para nada na query, mas precisa estar presente.

8) Faça um rebuild da solução

9) Teste a página

Agora falta permitir que ocorra uma mudança na ordenação quando os títulos forem clicados.

10) Selecione a gridview no design da página default.aspx

11) Na janela de propriedades, clique no raio dos eventos e dê um duplo clique no evento "sorting"

Vamos interceptar o momento em que o usuário pede uma nova ordenação - clicando em um dos títulos - para mudar nossa propriedade ordenação.

12) Monte o código do evento da seguinte forma :

Protected Sub GridView1_Sorting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles GridView1.Sorting
Select Case e.SortExpression.ToUpper
Case "CUSTOMERID"
Ordenacao = 1
Case "COMPANYNAME"
Ordenacao = 2
Case "CONTACTNAME"
Ordenacao = 3
Case "COUNTRY"
Ordenacao = 4
End Select
End Sub

Assim traduzimos a ordenação para o código numérico que utilizamos.

13) Teste a aplicação. Verá que a ordenação já encontra-se funcionando

Conclusão

Ficamos com a impressão que nenhuma tarefa é difícil o suficiente para o .NET, especialmente quando observamos o quão pouco de código tivemos que escrever. Porém, sem dúvida, tarefas como custom paging possuem uma série de pequenos truques que pegam os desprevinidos pelo pé.

Felizmente o Entity Framework, quando bem utilizado com o EntityDataSource no ASP.NET, já realiza custom paging por padrão, eliminando esta preocupação do desenvolvedor.





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 : BJZ7XZ9MlMy E-Mail : om6dhwqrs@hotmail.com
Muito bom Beto. Sem vocea jamais toimares estes momentos registrados. Espero que a ABES tenha como armazenar este acervo para no futuro relembramos o nosso passado. As coisas boas que estamos fazendo Uma abrae7o, Vitorio.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
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