Condividi tramite


Aggiornamento efficiente

Batch

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

var blog = context.Blogs.Single(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" });
context.SaveChanges();

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 IN edizione Standard RT e un'istruzione UPDATE. Anziché inviarli uno alla sola, man mano che vengono aggiunte istanze del blog, EF Core tiene traccia di queste modifiche internamente e le esegue in un singolo round trip quando SaveChanges viene chiamato.

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;
}
context.SaveChanges();

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 ExecuteUpdate metodi e ExecuteDelete per eseguire la stessa operazione in modo molto più efficiente:

context.Employees.ExecuteUpdate(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;

In questo UPDATE modo viene eseguita 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 comportano un sovraccarico aggiuntivo. Per altre informazioni, vedere ExecuteUpdate e ExecuteDelete.

Se si usa una versione precedente di EF Core che non supporta ExecuteUpdate ancora e ExecuteDeleteo 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 ExecuteUpdateExecuteDelete/, vedere la pagina Panoramica sul salvataggio dei dati.