LA PAGINAZIONE IN ASP.NET CORE

NET CORE

Dobbiamo limitare il numero di corsi visualizzati per pagina, estrarre dal database dieci corsi sarà estremamente più veloce che estrarne mille. Vediamo il piano per implementare la paginazione.

Piano

Aggiungiamo i link di paginazione di Bootstrap 5.3

Link

Vediamo il Tag Helper generato da asp-route-page.

Tag Helper

Vediamo come si effettua la paginazione con dialetto SQL con una slide. LIMIT è uguale a dieci perché ogni pagina deve contenere dieci risultati, OFFSET venti perché vogliamo ottenere la terza pagina; quindi, dobbiamo saltare i primi 20 risultati.

Paginazione

Vediamo come si effettua la paginazione con LINQ.

Paginazione con LINQ

ORDINARE I RISULTATI IN ELENCO

Vediamo il piano per implementare l’ordinamento, sulla paginazione torneremo dopo. A parte il fatto di avere due parametri orderby e ascending il resto poco cambia.

Ordinamento

Vediamo come si usa la clausola ORDER BY nel dialetto SQL. Quando non vi è alcuna indicazione il valore predefinito è ASC (ASCENDING)

Order By

Ci sono alcune problematiche nell’ordinamento che riporto nella slide. Innanzitutto, orderby deve essere sanitizzato, ascending è un bool mentre noi abbiamo bisogno delle stringhe ASC e DESC. orderby e ascending non possono diventare dei SqliteParameter, per questo ho creato una classe Sql.cs.

Order By

Vediamo di sistemare nel codice tutte queste problematiche. Al posto di ascending mettiamo direction che assume i valori ASC e DESC come voluto. Se non ci arriva un valore di orderby corretto lo sanitizziamo e usiamo i valori predefiniti definiti nella configurazione nel file appsettings.json.

Order By

Vediamo l’ordinamento con LINQ.

Ordinamento con LINQ

CREARE UN MODEL BINDING PERSONALIZZATO

Dopo aver realizzato una funzionalità occorre guardarsi indietro e capire se il codice può essere migliorato. Questa attività si chiama Refactoring ed è necessario farla perché scrivere codice è un’attività complessa, già arrivare ad una soluzione richiede un certo sforzo. Per esempio, dopo aver implementato queste tre funzionalità, mi rendo conto che la sanitizzazione è stata inserita all’interno dei servizi applicativi, e questo è sbagliato perché in questo momento tali servizi sono appesantiti di una responsabilità in più, mentre dovrebbero decidere solo cosa estrarre dal database.

LA SANITIZZAZIONE

Se la sanitizzazione fosse stata fatta più a monte, cioè quando l’utente inserisce i valori magari usando un model binding personalizzato forse sarebbe stata la soluzione più giusta. Vediamo innanzitutto dove risiede il problema guardando la classe AdoNetCourseService.

Sanitizzazione

La logica di sanitizzazione in questa classe è duplicata nell’altro servizio applicativo EfCoreCourseService e questo è sbagliato. Una possibile soluzione consiste nel creare una classe che si occupi di sanitizzare i valori e incentrare in un solo punto tutta questa parte.

Sanitizzazione
Sanitizzazione

Il problema di questa nuova classe è che avendo tolto i setter alle proprietà, questo perché non vogliamo che anche accidentalmente questi valori vengano cambiati esternamente, il model binder non funziona più. Dobbiamo costruirci un model binder personalizzato.

Model binder

Questo è il codice della classe CourseListInputModelBinder.

CourseListModelBinder

SPIEGAZIONE DEL MODEL BINDER

Il metodo “BindModelAsync” è definito nella classe “CourseListInputModelBinder” che implementa l’interfaccia “IModelBinder“. Questa classe è uno strumento di binding personalizzato, utilizzato da ASP.NET Core per associare i dati della richiesta HTTP ai parametri delle Action. In questo metodo, `bindingContext.ValueProvider` fornisce valori dalla richiesta HTTP I dati della richiesta vengono recuperati e trasformati in tipologie adeguate. Quindi, con questi dati viene creata una nuova istanza di `CourseListInputModel`.

CREAZIONE ISTANZA COURSE LIST INPUT MODEL

Questa nuova istanza viene quindi impostata come risultato per il contesto di associazione per indicare che il modello è stato associato correttamente. Infine, il metodo restituisce “Task.CompletedTask” perché è un metodo asincrono, ma in questo caso non viene eseguito alcun lavoro asincrono effettivo. Infine, questo raccoglitore di modelli personalizzati viene utilizzato nella classe “CourseListInputModel” utilizzando l’attributo “ModelBinder”. Pertanto, quando ASP.NET Core rileva un parametro di tipo “CourseListInputModel” in una Action, utilizzerà “CourseListInputModelBinder” per associare i dati della richiesta HTTP a tale parametro.

Model Binder

MANTENERE LO STATO NELLE VIEW

Le view Razor perdono memoria da una richiesta all’altra, al momento se digitiamo qualche stringa di ricerca nella casella di testo al submit il testo digitato viene perso. Adesso andremo a vedere come i nostri elementi HTML mantengano uno stato consistente attraverso varie richieste dell’utente.

Mantenere lo stato

Se l’utente ha digitato Chitarra nella casella di ricerca questo valore tramite il Model Binder Personalizzato arriva al controller che coinvolge la View per estrarre i corsi pertinenti ai criteri di ricerca. Tuttavia, la View non è al corrente di ciò che ha digitato l’utente, né dei criteri di ordinamento e link di paginazione. Vediamo come risolvere il problema.

Mantenere lo stato

In una View non possiamo usare più direttive @model ma possiamo incapsulare in un nuovo Model tutte le proprietà che ci servono, compreso l’input dell’utente e fornire questa nuova classe come @model.

CourseListViewModel

Ora come modello nella pagina Index dei corsi utilizzeremo CourseListViewModel che contiene tutte le informazioni di cui abbiamo bisogno. Vediamo come abbiamo valorizzato le proprietà nel Controller. Consulta il codice della View Razor Index per vedere come ho mantenuto lo stato.

Courses Controller

RENDERE DINAMICI I LINK DI PAGINAZIONE

Per implementare i link di paginazione abbiamo bisogno di conoscere il numero di righe totali dei corsi, cosa che il controller non ci sta restituendo. Anziché una List<CourseViewModel> che è quello che attualmente ci sta restituendo il Controller abbiamo bisogno di una nuova classe ListViewModel<CourseViewModel> che tenga conto del numero di record totali cercati nella casella di testo, oppure tutti se lasciata vuota. Scarica il codice sorgente e guarda l’implementazione di questa nuova classe nel controller e nei servizi applicativi.

ListViewModel

LINK AL CODICE SU GITHUB

GITHUB

Scaricare il codice della sezione13 o clonare il repository GITHUB per avere a disposizione tutte le sezioni nel tuo editor preferito.