Oggi in qualche esperimento ho notato una cosa interessante:
var dbContextOptionsBuilder = new DbContextOptionsBuilder<MyContext>();
dbContextOptionsBuilder.UseSqlServer(@"Data Source=LAPTOP-HBBAKRHO\SQLEXPRESS;Initial Catalog=myDb;Integrated Security=True");
var context = new MyContext(dbContextOptionsBuilder.Options);
Stopwatch stopWatch;
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
// CLOSE.
context.Dispose();
risultati:
Come puoi vedere, la prima richiesta richiede sempre più tempo. Perché succede?
Ho pensato di aprire / chiudere la connessione al database ORM per ogni richiesta, forse non è così e la connessione aperta EF Core solo per la prima volta e usarla per tutte le richieste successive fino a quando DbContext
?
Ogni volta si ottengono dati con lo stesso parametro dalla stessa istanza di contesto, pertanto la seconda e la terza richiesta get non andranno affatto al server SQL. EF ha la propria cache di primo livello di tutte le entità caricate dal database. Ecco perché la seconda e la terza volta sono molto più veloci. Se si modifica il parametro 12345 per la seconda query con un altro, sarà più veloce del primo, ma non così veloce, poiché richiederà i dati dal server.
Vi consiglierei di leggere questi argomenti sulle esecuzioni e le prestazioni di EF:
La prima richiesta è più lenta perché Entity Framework deve creare le viste di mappatura dal modello. Questo si chiama "compilazione del modello". Ciò accade quando si esegue la prima query.
Quindi nel tuo esperimento la prima query dovrebbe essere lenta anche se non restituisce alcun record.
Quindi se provi qualcosa del genere:
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -2); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
La prima query sarà molto più lenta della seconda anche se non restituisce alcun record.
Quando ora prendi un'altra entità dal tuo contesto (immaginiamo di avere un dbset chiamato dipendenti nel tuo contesto) ed esegui la prima query su questo dbset ed esegui le query su progetti dopo, vedrai che le query dei progetti verranno eseguite molto Più veloce.
stopWatch = Stopwatch.StartNew();
context.Employees.AsNoTracking().SingleOrDefault(e => e == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -2); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
Un modo per aggirare questo problema è eseguire una query "falsa" durante l'avvio in modo che la prima query eseguita dall'utente non sia lenta