Per realizzare applicazioni che rendano i nostri utenti entusiasti, non dobbiamo solo fare in modo che il nostro codice "funzioni" ma dovremmo anche garantire buone performance e pagine che si caricano rapidamente. Oltretutto, se realizziamo un'applicazione che esige poche risorse dal server, aiuteremo il nostro committente a ridurre i costi di funzionamento.
MiniProfiler è uno strumento che può aiutarci a realizzare questi obiettivi, perché serve a "profilare", cioè esaminare il comportamento della nostra applicazione per produrre un report HTML, compatto ma informativo, che rende evidenti le possibili inefficienze della nostra applicazione.
Con esso possiamo tracciare sia i tempi di esecuzione delle nostre routine che il numero di query SQL che inviamo al database.
Installare MiniProfiler
Iniziamo installando i pacchetti di MiniProfiler con i seguenti comandi. Il primo pacchetto è strettamente necessario, mentre il secondo lo referenziamo solo se nella nostra applicazione stiamo usando Entity Framework Core.dotnet add package MiniProfiler.AspNetCore.Mvc dotnet add package MiniProfiler.EntityFrameworkCore
Poi rechiamoci nel metodo Configure della classe Startup e configuriamo il middleware di MiniProfiler.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseMiniProfiler(); //Qui usiamo altri middleware }
Restiamo nella classe Startup e andiamo anche ad aggiungere i suoi servizi nel metodo ConfigureServices.
public void ConfigureServices(IServiceCollection services) { services.AddMiniProfiler() .AddEntityFramework(); //Aggiungiamo questa riga solo se usiamo Entity Framework Core //Qui aggiungiamo altri servizi }
MiniProfiler dispone di tante opzioni di configurazione che possiamo fornire al metodo AddMiniProfiler. Ad esempio possiamo decidere selettivamente quali richieste profilare e come vogliamo che siano mostrate le informazioni. Tutto ciò lo troviamo documentato all'indirizzo https://miniprofiler.com/dotnet/AspDotNetCore
Per completare l'installazione, apriamo la view /Views/Shared/_Layout.cshtml e inseriamo il tag helper di MiniProfiler, che verrà usato per sovraimporre il report HTML alle nostre view. Posizioniamolo subito prima della chiusura del tag .
<!DOCTYPE html> <html> <head> <!-- Omissis --> </head> <body> <!-- Omissis --> <mini-profiler /> </body> </html>
Infine andiamo in /Views/_ViewImports.cshtml e registriamo il tag helper in modo che possa essere elaborato dal view engine Razor.
@addTagHelper *, MiniProfiler.AspNetCore.Mvc
Ora siamo finalmente pronti per iniziare a profilare la nostra applicazione ASP.NET Core.
Profilare le query LINQ di Entity Framework Core
Supponiamo di voler verificare il comportamento di una query LINQ che estre alcuni prodotti e da cui selezioniamo la categoria. Per profilare il codice, avvolgiamolo con l'istruzione MiniProfiler.Current.Step("Descrizione").using (MiniProfiler.Current.Step("Recupero categorie")) { var categories = new HashSet<Category>(); //db è un riferimento all'istanza del nostro DbContext var products = await db.Products.Where(p => p.Amount > 1000) .ToListAsync(); foreach (var product in products) { categories.Add(product.Category); } }
Se abbiamo il lazy loading abilitato, questo codice produrrà numerose query al database: una per recuperare i prodotti e altre n per recuperare la categoria di ciascun prodotto. Questo è anche noto come problema Select n+1 e potrebbe passare inosservato se non prestassimo attenzione alle query inviate da Entity Framework Core. Con MiniProfiler, il problema risulta subito evidente perché nella ci mostra una linguetta nella parte alta a sinistra della pagina. Essa riporta il tempo di esecuzione con un punto esclamativo, a segnalare una situazione anomala. Lo possiamo cliccare per avere dettagli aggiuntivi e scoprire che in questo caso stiamo inviando ben 38 query SQL, come si nota dall'immagine.
Cliccando il 38 possiamo entrare nel dettaglio per scoprire quali sono le query in questione. Qui sono anche evidenti i momenti in cui apriamo e chiudiamo la connessione.
Comprendere il problema è solo il primo passo ma ci mette in condizione di pensare a una soluzione migliore, che in questo caso consiste nel modificare la query LINQ così.
var categories = await db.Products.Where(p => p.Amount > 1000) .Select(p => p.Category) .Distinct() .ToListAsync();
Profilare le query di ADO.NET
Anche usando ADO.NET possiamo sfruttare MiniProfiler per misurare il tempo di esecuzione delle query. Per attivare il profiler, in questo caso dobbiamo anche crearci un oggetto StackExchange.Profiling.Data.ProfiledDbConnection e passargli la connessione nel costruttore, come si vede nel seguente esempio.using (MiniProfiler.Current.Step("Query ADO.NET")) { using (var conn = new StackExchange.Profiling.Data.ProfiledDbConnection( new SqlConnection(connString), MiniProfiler.Current)) { await conn.OpenAsync(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT * FROM Products, Categories WHERE Products.Amount > 1000"; using (var reader = await cmd.ExecuteReaderAsync()) { //Omissis } } } }
In questo caso vediamo appunto che la query (una CROSS JOIN non corretta) sta richiedendo più di 1 secondo e quindi dovremmo valutare se abbiamo risultati migliori da una LEFT JOIN e dall'aggiunta di indici.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare EF.Constant per evitare la parametrizzazione di query SQL
Utilizzare una qualunque lista per i parametri di tipo params in C#
Gestire eccezioni nei plugin di Semantic Kernel in ASP.NET Core Web API
Utilizzare i primary constructor di C# per inizializzare le proprietà
Gestire la cancellazione di una richiesta in streaming da Blazor
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
gRPC con .NET
Aggiornare a .NET 9 su Azure App Service
Effettuare il log delle chiamate a function di GPT in ASP.NET Web API
Autenticarsi in modo sicuro su Azure tramite GitHub Actions
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
I più letti di oggi
- Utilizzare una qualunque lista per i parametri di tipo params in C#
- Includere un button in un component ed esporne l'evento click in Angular
- #SQLServer #Modeling (codename Oslo) CTP di nov 2009 aggiornato per VS 2010 RC: http://u.aspitalia.com/fi
- .NET Conference Italia 2019 Live - Milano
- Accedere a file XML remoti con ASP.NET
- Creare una libreria CSS universale: Nav menu
- Utilizzare funzioni di istanza con le Azure Function
- A quick tour around Azure Dev Spaces