Con Alessio si stava disquisendo sull'utilità dell'object data source, e ho pensato fosse meglio rispondere con un nuovo post, data la mia prolissità :)
Per quanto riguarda la paginazione, per dirla alla Pialorsi (mi pare fosse suo un articolo in merito, ma non riesco più a trovarlo), se restituisci troppi risultati, significa che c'è qualche problema (io non vado quasi mai oltre la seconda pagina di Google): il pb è che i filtri non sono sufficienti a dare un risultato accettabile per l'utente... ma ci possiamo trovare nelle condizioni in cui l'utente vuole vedere tutto lo scibile, per cui facciamolo nel modo più efficiente possibile.
Mi sono trovato nella condizione di dover paginare. Non l'ho mai fatto fare al datagrid/gridview (se non per piccole demo o solo se ero sicuro che le righe non sarebbero mai state più di qualche decina).
Lo facevo utilizzando una stored che accettava le clausole di filtro e di ordinamento, pagina corrente e nr risultati per pagina.
La stored inseriva in una tabella temporanea le pk dei record risultato della query. La tabella temporanea aveva 2 colonne: un id autoincrement ed un id di tipo identico alla primary key della tabella oggetto della query.
Poi faceva una select della tabella temporanea in join con la tabella principale, filtrando solo le righe che avevano id (della tabella temporana) compresi fra i valori desiderati (current_page*page_size<=ID<(current_page+1)*page_size).
Ultima cosa: restituiva anche il totale risultati e il nr di pagine.

Ecco un esempio (era un po' che non la toccavo, quindi sono andato a ripescarla da un vecchio backup per modificarla opportunamente):

ALTER PROCEDURE query_sulla_tabella_TEST_TABLE
@TITOLO VARCHAR(40), --una possibile condizione di filtro
@PAGE_SIZE INT,
@PAGE_NUM INT
AS
DECLARE @START INT
DECLARE @STOP INT

SET NOCOUNT ON

--ammesso che il tipo INT sia il tipo della primary key della tabella TEST_TABLE
CREATE TABLE #RISULTATI(NUM INT IDENTITY(1,1),ID INT)

--inserisce nella tabella temporanea i risultati della query
INSERT INTO #RISULTATI(ID) SELECT pk_id FROM TEST_TABLE WHERE TITOLO=@TITOLO

--determina gli estremi
SET @START=((@PAGE_NUM-1)*@PAGE_SIZE)+1
SET @STOP=@START+@PAGE_SIZE-1

--fa la select finale con il join
SELECT *
FROM
TEST_TABLE
INNER JOIN
(SELECT NUM, ID FROM #RISULTATI WHERE NUM>=@START AND NUM <=@STOP)
tmp N pk_id=tmp.ID
WHERE TITOLO=@TITOLO

--restituisce il totale dei risultati e delle pagine
SELECT
COUNT(*) AS TOT_RECORD,
CEILING(COUNT(*)/@PAGE_SIZE) AS TOT_PAGINE
FROM
#RISULTATI

--elimina la tabella temporanea
DROP TABLE #RISULTATI

SET NOCOUNT OFF

Spero sia abbastanza chiaro.
In realtà avevamo (il merito non è soltanto mio) creato una stored che costruiva (mediante concatenazione di varchar) una query a run-time ed eseguiva tutto mediante exec. In pratica passavamo anche il nome della tabella da cui filtrare, le condizioni di filtro, le clausole di ordinamento, ecc... Ma non è importante ai fini dell'esempio.

In questa stored recepivamo un bel po' di "best practices" raccolte in giro, fra cui un paio di articoli dei ragazzi di DevLeap (che non trovo più, hanno i link rotti :( ).
In breve esistevano diverse ipotesi, fra cui anche quella di usare un cursore lato server (in Sql Server 2000) o query innestate...

Per diverso tempo ho usato MySql come db "ludico", quindi mi sono adagiato sugli allori e sul fatto che questo db gestisse la paginazione con il LIMIT.
Anche SqlServer 2005 gestisce la paginazione lato server.
Da quando uso NHibernate... cerco di far fare tutto a lui ;) Dite che sono viziato?

[Edit:articolo devleap]

Technorati :