Condividi tramite


Aggiornamento efficiente

Raggruppamento

EF Core consente di ridurre al minimo i round trip raggruppando automaticamente tutti gli aggiornamenti in un singolo round trip. Considerare quanto segue:

var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();

Il file precedente carica un blog dal database, ne modifica l'URL e quindi aggiunge due nuovi blog; per applicare questa operazione, al database vengono inviate due istruzioni SQL INSERT e un'istruzione UPDATE. Anziché inviarli uno alla volta, man mano che vengono aggiunte istanze del Blog, EF Core tiene traccia di queste modifiche internamente e le esegue in un singolo roundtrip quando viene chiamato SaveChanges.

Il numero di istruzioni eseguite in batch di Entity Framework in un singolo round trip dipende dal provider di database in uso. Ad esempio, l'analisi delle prestazioni ha mostrato che l'invio in batch è in genere meno efficiente per SQL Server quando sono coinvolte meno di 4 istruzioni. Analogamente, i vantaggi dell'invio in batch diminuiscono dopo circa 40 istruzioni per SQL Server, pertanto EF Core eseguirà per impostazione predefinita solo fino a 42 istruzioni in un singolo batch ed eseguirà istruzioni aggiuntive in round trip separati.

Gli utenti possono anche modificare queste soglie per ottenere prestazioni potenzialmente più elevate, ma eseguire attentamente il benchmark prima di modificarle:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

Usare ExecuteUpdate e ExecuteDelete quando pertinente

Si supponga di voler dare a tutti i dipendenti un aumento. Un'implementazione tipica di questo in EF Core sarà simile alla seguente:

foreach (var employee in context.Employees)
{
    employee.Salary += 1000;
}
await context.SaveChangesAsync();

Anche se questo codice è perfettamente valido, analizziamo le operazioni che esegue dal punto di vista delle prestazioni:

  • Viene eseguito un round trip del database per caricare tutti i dipendenti pertinenti; si noti che porta tutti i dati di riga dei dipendenti al cliente, anche se sarà necessario solo lo stipendio.
  • Il rilevamento delle modifiche di EF Core crea snapshot durante il caricamento delle entità e quindi confronta gli snapshot con le istanze per scoprire quali proprietà sono state modificate.
  • In genere, viene eseguito un secondo round trip del database per salvare tutte le modifiche (si noti che alcuni provider di database suddivideno le modifiche in più round trip). Anche se questo comportamento di invio in batch è molto meglio di eseguire un round trip per ogni aggiornamento, EF Core invia comunque un'istruzione UPDATE per dipendente e il database deve eseguire ogni istruzione separatamente.

A partire da EF Core 7.0, è possibile usare i metodi ExecuteUpdateAsync e ExecuteDeleteAsync per eseguire la stessa operazione in modo molto più efficiente:

await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

In questo modo viene inviata l'istruzione SQL seguente al database:

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Questa UPDATE esegue l'intera operazione in un singolo round trip, senza caricare o inviare dati effettivi al database e senza usare i macchinari di rilevamento delle modifiche di EF, che impongono un sovraccarico aggiuntivo. Per altre informazioni, vedere ExecuteUpdate e ExecuteDelete.

Se si usa una versione precedente di EF Core che non supporta ancora ExecuteUpdate e ExecuteDeleteoppure si vuole eseguire un'istruzione SQL complessa che non è supportata da questi metodi, è comunque possibile usare una query SQL per eseguire l'operazione:

context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Per altre informazioni sulle differenze tra SaveChanges e ExecuteUpdate/ExecuteDelete, vedere la pagina panoramica sul salvataggio dei dati.