O grande problema, é que retornamos ao cliente registros indispensáveis para página corrente, o que de certa forma, causa um grande tráfego na rede para no final ser desconsiderado. Sendo assim, retornar todos os registros, e armazenar em uma variável de sessão, também é muito custoso, talvez quando o acesso é mínimo, não se perceba, mas quando a quantidade de usuários requisitando a mesma informação, isso poderá ocasionar sérios problemas de performance.
Tendo este cenário e/ou problema, a solução seria retornarmos da base de dados somente os registros correspondentes a página qual o usuário requerer. Além de diminuir o tráfego de dados na rede, isso nos proporcionará um grande ganho de performance. O artigo mostrará como retornar somente os registros da base de dados qual se enquadram com a página que o usuário requerer. Isso será feito através de uma Stored Procedure, utilizando a tabela Customers da base de dados Northwind como exemplo. Através de um SqlDataReader, recuperamos o result set retornado pela Stored Procedure e populamos o controle DataList.
O primeiro passo e construirmos a Stored Procedure para nos retornar os registros que necessitamos. Com isso, precisamos informar a ela a pagina requerida pelo Usuário, a quantidade de registros à ser exibido por página e também criarmos um parâmetro de OUTPUT para sabermos a quantidade total de registros, que é necessário para efetuar os cálculos no cliente, para que seja possível manipular corretamente a navegação entre as páginas. Abaixo a Stored Procedure responsável por devolver apenas os registros que necessitamos:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
|
|
CREATE PROCEDURE ResgatarClientes |
|
@QtdePagina As Int, |
@PagAtual As Int, |
@TotalClientes As Int OUTPUT |
|
AS |
|
DECLARE @ContactName As Varchar(50) |
DECLARE @City As Varchar(50) |
DECLARE @QtdeInicial As Int |
DECLARE @Contador As Int |
|
SET @QtdeInicial = 0 |
SET @Contador = 0 |
|
SET NOCOUNT ON |
|
CREATE TABLE #ClientesTemp |
( |
ContactName Varchar(50) |
City Varchar(50) |
) |
|
DECLARE curPaginacaoClientes CURSOR FAST_FORWARD FOR |
SELECT ContactName, City FROM Customers ORDER BY ContactNameASC |
|
OPEN curPaginacaoClientes |
FETCH NEXT FROM curPaginacaoClientes |
INTO @ContactName, @City |
|
WHILE @@FETCH_STATUS = 0 |
BEGIN |
IF @QtdeInicial >= (@PagAtual * @QtdePagina) – @QtdePagina |
BEGIN |
INSERT INTO #ClientesTemp VALUES(@ContactName, @City) |
SET @Contador = @Contador + 1 |
|
IF @Contador >= @QtdePagina |
BREAK |
END |
SET @QtdeInicial = @QtdeInicial + 1 |
|
FETCH NEXT FROM curPaginacaoClientes |
INTO @ContactName, @City |
END |
|
CLOSE curPaginacaoClientes |
DEALLOCATE curPaginacaoClientes |
|
SELECT ContactName, City FROM #ClientesTemp |
|
DROP TABLE #ClientesTemp |
|
SET NOCOUNT OFF |
|
SET @TotalClientes = (SELECT COUNT(CustomerID) FROM Customers) |
GO |
|
|
Código 1 – Stored Procedure “ResgataClientes”. |
Como vemos no código acima, a Stored Procedure recebe dois parâmetros que é a página requisitada pelo Usuário e a quantidade de registros por página. Além disso, como dito anteriormente, existe um parâmetro de OUTPUT chamado @TotalClientes que e responsável por retornar a quantidade de Clientes para que seja possível efetuar os cálculos e assim exibindo corretamente a navegação da paginação.
Podemos ver também que é criado uma tabela temporária na linha 19, chamada #ClientesTemp, que é utilizada posteriormente, sendo populada com os registros necessários. Depois disso é criado um Cursor na linha 25 e através dele percorremos a tabela Customers e preenchemos a tabela temporária, anteriormente criada. Feito isso, fechamos o Cursor e o retiramos da memória, e retornamos ao cliente o resultado de um SELECT efetuado nesta tabela temporária na linha 51 e depois apagamos esta tabela (linha 53). E finalmente, apenas atribuimos ao parâmetro de saída a quantidade de registros da tabela Customers na linha 57.
Ainda é possível testá-la no Query Analyser, através do seguinte código:
|
|
DECLARE @TotalRegistros As Int |
EXECUTE ResgatarClientes 10, [N], @TotalClientes = @TotalRegistros OUTPUT |
PRINT @TotalRegistros |
|
|
Código 2 – Testando a Stored Procedure “ResgataClientes” no Query Analyser. |
Como vemos no código acima, o número 10 é a quantidade de registros por página e o [N] refere-se a página que o cliente está requerendo.
Feito isso, o próximo passo é partir para a aplicação ASP.NET, que irá utilizar esta Stored Procedure para popular e paginar os dados em um controle do tipo DataList. Devemos apenas arrastar em nosso WebForm o DataList e quatro controles do tipo LinkButton, que serão responsáveis pela navegação do usuário. Vamos incluir em nosso WebForm um Data Member que será responsável por armazenar a quantidade de registros por páginas:
|
|
Private _totalRegistrosPagina As Integer = 10 |
|
|
Código 3 – Definindo através de um Data Member a quantidade de registros por página. |
Logo, temos que criar um procedimento que será responsável por popular o DataList. Esta função se chamará CarregaGrid, e terá como parâmetro um valor inteiro representando a página que o usuário está requerendo. Vejamos abaixo o código deste procedimento:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
|
|
Private Sub CarregaGrid(ByVal paginaAtual As Integer) |
Dim conn As New SqlConnection(“CONNECTION_STRING”) |
Dim dr As SqlDataReader |
Dim cmd As New SqlCommand(“ResgatarClientes”, conn) |
cmd.CommandType = CommandType.StoredProcedure |
|
Dim paramQtdeRegistroPagina As SqlParameter |
paramQtdeRegistroPagina = New SqlParameter(“@QtdePagina”, SqlDbType.Int) |
paramQtdeRegistroPagina.Value = Me._totalRegistrosPagina |
cmd.Parameters.Add(paramQtdeRegistroPagina) |
|
Dim paramPaginaAtual As SqlParameter |
paramPaginaAtual = New SqlParameter(“@PagAtual”, SqlDbType.Int) |
paramPaginaAtual.Value = paginaAtual |
cmd.Parameters.Add(paramPaginaAtual) |
|
Dim paramTotalRegistros As SqlParameter |
paramTotalRegistros = New SqlParameter(“@TotalClientes”, SqlDbType.Int) |
paramTotalRegistros.Direction = ParameterDirection.Output |
cmd.Parameters.Add(paramTotalRegistros) |
|
Try |
conn.Open() |
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection) |
If dr.HasRows() Then |
With Me.dtlDados |
.DataSource = dr |
.DataBind() |
End With |
Me.lblPaginaCorrente.Text = paginaAtual |
End If |
Catch ex As Exception |
Response.Write(ex.ToString) |
Finally |
If Not (dr Is Nothing) Then |
dr.Close() |
End If |
Call Me.ControlePaginacao(cmd.Parameters(“@TotalClientes”).Value) |
End Try |
End Sub |
|
|
Código 4 – Procedimento CarregaGrid(). |
Analisando o código acima, criamos os parâmetros referente à quantidade de registros por página e a página requerida pelo Usuário e também definimos o parâmetro de OUTPUT chamado @TotalClientes, qual retorna a quantidade total de registros. Depois disso, verificamos se é retornado algum registro através da propriedade HasRows(), que retorna True, caso encontrado algum registro, e com isso populamos o DataList com este DataReader, e depois de fechado o DataReader, recuperamos o valor do parâmetro OUTPUT e invocamos o procedimento ControlePaginacao, que é responsável por exibir a quantidade de páginas e a página atual para o Usuário.
O procedimento ControlePaginacao recebe como parâmetro a quantidade total de Clientes, e baseado nisso, exibe a quantidade total de páginas e a página atual em que o Usuário se encontra:
|
|
Private Sub ControlePaginacao(ByVal totalClientes As Integer) |
If (totalClientes Mod Me._totalRegistrosPagina) = 0 Then |
Me.lblTotal.Text = totalClientes / Me._totalRegistrosPagina |
Else |
Me.lblTotal.Text = Convert.ToInt32((totalClientes / Me._totalRegistrosPagina) + 1) |
End If |
End Sub |
|
|
Código 5 – Procedimento ControlePaginacao. |
Agora, no evento Page_Load do WebForm, devemos chamar o procedimento CarregaGrid, passando como parâmetro o número 1, que refere-se a primeira página, lembrando que devemos chamar este procedimento quando não for PostBack. O código abaixo ilustra isso:
|
|
Private Sub Page_Load(…) Handles MyBase.Load |
If Not Page.IsPostBack Then |
Call Me.CarregaGrid(1) |
Call Me.Navegacao(1) |
End If |
End Sub |
|
|
Código 6 – Evento Page_Load do WebForm. |
Ainda temos mais um procedimento para criar, chamado Navegacao, procedimento qual se encarrega de habilitar/desabilitar os botões de navegação da paginação. Este procedimento também é baseado na página atual que está sendo exibida. Vejamos abaixo o código deste procedimento na íntegra:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
|
|
Private Sub Page_Load(ByVal paginaAtual As Integer) |
If paginaAtual = 1 Then |
With Me |
.lnkAnterior.Enabled = False |
.lnkProxima.Enabled = True |
.lnkPrimeiraPagina.Enabled = False |
.lnkUltimaPagina.Enabled = True |
End With |
ElseIf paginaAtual = Me.lblTotal.Text AndAlso paginaAtual > 1 Then |
With Me |
.lnkAnterior.Enabled = True |
.lnkProxima.Enabled = False |
.lnkPrimeiraPagina.Enabled = True |
.lnkUltimaPagina.Enabled = False |
End With |
Else |
With Me |
.lnkAnterior.Enabled = True |
.lnkProxima.Enabled = True |
.lnkPrimeiraPagina.Enabled = True |
.lnkUltimaPagina.Enabled = True |
End With |
End If |
End Sub |
|
|
Código 7 – Procedimento Navegacao. |
Depois disso, nosso controle ficará semelhante à imagem abaixo:

|
Figura 1 – Controle DataList já com os controles de Navegação pelas páginas. |
Mas ainda resta codificarmos os eventos Click() dos LinkButton responsáveis pela navegação das páginas. Temos quatro LinkButtons com as seguintes funcionalidades:
- < – Primeira página.
- << – Página anterior.
- >> – Próxima página.
- > – Última página.
|
|
Private Sub lnkPrimeiraPagina_Click(…) Handles lnkPrimeiraPagina.Click |
Call Me.CarregaGrid(1) |
Call Me.Navegacao(1) |
End Sub |
|
|
Código 8 – Procedimento que retorna à primeira página. |
|
|
Private Sub lnkUltimaPagina_Click(…) Handles lnkUltimaPagina.Click |
Call Me.CarregaGrid(Convert.ToInt32(Me.lblTotal.Text)) |
Call Me.Navegacao(Convert.ToInt32(Me.lblTotal.Text)) |
End Sub |
|
|
Código 9 – Procedimento que vai para a última página. |
|
|
Private Sub lnkAnterior_Click(…) Handles lnkAnterior.Click |
Dim paginaAtual As Integer = Integer.Parse(Me.lblPaginaCorrente.Text.ToString()) – 1 |
Call Me.CarregaGrid(Convert.ToInt32(paginaAtual) |
Call Me.Navegacao(Convert.ToInt32(paginaAtual) |
End Sub |
|
|
Código 10 – Procedimento que retorna à página anterior. |
|
|
Private Sub lnkProxima_Click(…) Handles lnkProxima.Click |
Dim paginaAtual As Integer = Integer.Parse(Me.lblPaginaCorrente.Text.ToString()) + 1 |
Call Me.CarregaGrid(Convert.ToInt32(paginaAtual) |
Call Me.Navegacao(Convert.ToInt32(paginaAtual) |
End Sub |
|
|
Código 11 – Procedimento vai para a página posterior. |
Conclusão: Vimos neste artigo como resgatar os dados da base de dados através de uma Stored Procedure, mas limitando os dados da página a ser exibida para proporcionar performance, retornando os dados para uma aplicação ASP.NET, e populando um controle DataList, controlando e exibindo a paginação.
PaginandoDadosStoredProcedure.zip (22.51 kb)
Curtir isso:
Curtir Carregando...
Poxa amigão obrigado por responder tão rapido , mas no meu caso tem qie ser o DataList pois pretendo fazer uma galeria de Imagens com com uma tabela de 10 linhas e 3 colunas, o GridView conheço bem mas não resolve meu problema obrigado pela dica.
Boa Noite,
Israel, muito bom seu artigo, porem sou programador C# e não consigo fazer o mesmo na minha linguagem tem como você postar esse mesmo artigo em C#? fiz passo a passo mas não funciona andei usando componente mas não é a mesma coisa mesmo pq a maioria é pago prefiro aprender fazer na mão se tiver algum modelo em C# fico grato , obrigado.
Boas Heraldo,
Recorre ao controle GridView que o ASP.NET já traz, que já possui a paginação nativa, onde com pequenas configurações, você atinge o mesmo resultado.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!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>Untitled Page</title>
<style type="text/css">
.style1
{
width: 47%;
}
</style>
</head>
<body>
<form id="form1" runat="server">
CustomerID:
‘ />
ContactName:
‘ />
City:
‘ />
”
SelectCommand=”SELECT [CustomerID], [ContactName], [City] FROM [Customers]”>
<Primeira
<Anterior
Proximo>
Ultima>>
</form>
</body>
</html>
olha o link pra ter uma ideia, esse é com conponente da DevExpress
Por isso motivo que tem que ser DataList
Desculpa os erros de português também rs…vou tentar resolver isso, se conseguir gostaria que publicasse a solução no seu blog para que outros possam utilizar o modelo , pois como comentei antes não achei nada em C# que funcionasse com a simplicidade que seu em VB.NET funciona ok, Valew.
Bom Dia,
Olá Israel tudo bem? resolvido o problema de paginação em C# com base no seu modelo, um amigo achou o erro que eu não tinha visto alias dois erros segue modelo, para analise.
Valew.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using System.Data;
public partial class _Default : System.Web.UI.Page
{
string _connectionString = "Data Source=NS01111;Initial Catalog=DADOS;User ID=admin;Password=admin";
int _totalRegistrosPagina = 10;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
CarregaGrid(1);
Navegacao(1);
}
}
public void CarregaGrid(int paginaAtual)
{
SqlConnection conn = new SqlConnection(_connectionString);
SqlDataReader dr;
SqlCommand cmd = new SqlCommand("ResgatarClientes", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter paramQtdeRegistroPagina;
paramQtdeRegistroPagina = new SqlParameter("@QtdePagina", SqlDbType.Int);
paramQtdeRegistroPagina.Value = _totalRegistrosPagina;
cmd.Parameters.Add(paramQtdeRegistroPagina);
SqlParameter paramPaginaAtual;
paramPaginaAtual = new SqlParameter("@PagAtual", SqlDbType.Int);
paramPaginaAtual.Value = paginaAtual;
cmd.Parameters.Add(paramPaginaAtual);
SqlParameter paramTotalRegistros;
paramTotalRegistros = new SqlParameter("@TotalClientes", SqlDbType.Int);
paramTotalRegistros.Direction = ParameterDirection.Output;
cmd.Parameters.Add(paramTotalRegistros);
try
{
conn.Open();
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (dr.HasRows == true)
{
dtlDados.DataSource = dr;
dtlDados.DataBind();
lblPaginaCorrente.Text = paginaAtual.ToString();
}
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
finally
{
/* if (dr.HasRows == false)
{
dr.Close();
}*/
ControlePaginacao(int.Parse(paramTotalRegistros.Value.ToString()));
}
}
public void ControlePaginacao(int totalClientes)
{
if (totalClientes % _totalRegistrosPagina == 0)
{
lblTotal.Text = (totalClientes / _totalRegistrosPagina).ToString();
}
else
{
lblTotal.Text = ((totalClientes / _totalRegistrosPagina) + 1).ToString();
}
}
private void Navegacao(int paginaAtual)
{
if (paginaAtual == 1)
{
{
lnkAnterior.Enabled = false;
lnkProxima.Enabled = true;
lnkPrimeiraPagina.Enabled = false;
lnkUltimaPagina.Enabled = true;
}
}
else if (paginaAtual == Convert.ToUInt32(lblTotal.Text) && paginaAtual > 1)
{
{
lnkAnterior.Enabled = true;
lnkProxima.Enabled = false;
lnkPrimeiraPagina.Enabled = true;
lnkUltimaPagina.Enabled = false;
}
}
else
{
{
lnkAnterior.Enabled = true;
lnkProxima.Enabled = true;
lnkPrimeiraPagina.Enabled = true;
lnkUltimaPagina.Enabled = true;
}
}
}
protected void lnkPrimeiraPagina_Click(object sender, EventArgs e)
{
}
protected void lnkUltimaPagina_Click(object sender, EventArgs e)
{
}
protected void lnkAnterior_Click(object sender, EventArgs e)
{
}
protected void lnkProxima_Click(object sender, EventArgs e)
{
}
protected void lnkProxima_Click1(object sender, EventArgs e)
{
int paginaAtual = int.Parse(lblPaginaCorrente.Text.ToString()) + 1;
CarregaGrid(paginaAtual);
Navegacao(paginaAtual);
}
protected void lnkUltimaPagina_Click1(object sender, EventArgs e)
{
CarregaGrid(Convert.ToInt32(lblTotal.Text));
Navegacao(Convert.ToInt32(lblTotal.Text));
}
protected void lnkAnterior_Click1(object sender, EventArgs e)
{
int paginaAtual = int.Parse(lblPaginaCorrente.Text.ToString()) – 1;
CarregaGrid(paginaAtual);
Navegacao(paginaAtual);
}
protected void lnkPrimeiraPagina_Click1(object sender, EventArgs e)
{
CarregaGrid(1);
Navegacao(1);
}
}
aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!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">
.style2
{
height: 33px;
}
.style1
{
height: 25px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<table align="center" width="100%">
<tr>
<td align="center">
<asp:DataList ID="dtlDados" runat="server" BackColor="White"
BorderColor="#999999" BorderStyle="None" BorderWidth="1px" CellPadding="3"
GridLines="Vertical" Width="100%">
<SelectedItemStyle BackColor="#008A8C" Font-Bold="True" ForeColor="White" />
<AlternatingItemStyle BackColor="#DCDCDC" />
<ItemStyle BackColor="#EEEEEE" ForeColor="Black" />
<ItemTemplate>
<table align="center" width="100%">
<tr>
<td width="50%">
<%# DataBinder.Eval(Container.DataItem, "ContactName")%>
</td>
<td width="50%">
<%# DataBinder.Eval(Container.DataItem, "City")%>
</td>
</tr>
</table>
</ItemTemplate>
<FooterStyle BackColor="#CCCCCC" ForeColor="Black" />
<HeaderStyle BackColor="#000084" Font-Bold="True" ForeColor="White" />
<AlternatingItemTemplate>
<table align="center" width="100%">
<tr>
<td width="50%">
<%# DataBinder.Eval(Container.DataItem, "ContactName")%>
</td>
<td width="50%">
<%# DataBinder.Eval(Container.DataItem, "City")%>
</td>
</tr>
</table>
</AlternatingItemTemplate>
</asp:DataList>
</td>
</tr>
<tr>
<td align="right" class="style2">
<table cellpadding="2" cellspacing="2">
<tr>
<td class="style1">
(Pagina
<asp:Label ID="lblPaginaCorrente" Runat="server"></asp:Label>
de
<asp:Label ID="lblTotal" Runat="server"></asp:Label>
)
</td>
<td class="style1">
<asp:LinkButton ID="lnkPrimeiraPagina" runat="server"
onclick="lnkPrimeiraPagina_Click1">< Primeira</asp:LinkButton>
<asp:LinkButton ID="lnkAnterior" Runat="server" onclick="lnkAnterior_Click1"
title="Click here to display the previous page of clients."><< Anterior</asp:LinkButton>
<asp:LinkButton ID="lnkProxima" Runat="server" onclick="lnkProxima_Click1"
title="Click here to display the next page of clients."> Proxima >></asp:LinkButton>
<asp:LinkButton ID="lnkUltimaPagina" runat="server"
onclick="lnkUltimaPagina_Click1">Ultima ></asp:LinkButton>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>
Comenta se souber de outro modo de implementar paginação com DataList sem Usar Procedure.