Salvataggio di dati

Durante l'esecuzione di query è possibile leggere i dati dal database, il salvataggio dei dati implica l'aggiunta di nuove entità al database, la rimozione di entità o la modifica delle proprietà delle entità esistenti in qualche modo. Entity Framework Core (EF Core) supporta due approcci fondamentali per il salvataggio dei dati nel database.

Approccio 1: rilevamento delle modifiche e SaveChanges

In molti scenari, il programma deve eseguire query su alcuni dati dal database, apportare alcune modifiche e salvare le modifiche. questo è talvolta definito "unità di lavoro". Si supponga, ad esempio, di avere un set di blog e di modificare la Url proprietà di una di esse. In Entity Framework questa operazione viene in genere eseguita come segue:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Single(b => b.Url == "http://example.com");
    blog.Url = "http://example.com/blog";
    context.SaveChanges();
}

Il codice precedente esegue i passaggi seguenti:

  1. Usa una normale query LINQ per caricare un'entità dal database (vedere Eseguire query sui dati). Le query di Entity Framework vengono monitorate per impostazione predefinita, ovvero EF tiene traccia delle entità caricate nel relativo rilevamento delle modifiche interne.
  2. L'istanza di entità caricata viene modificata come di consueto assegnando una proprietà .NET. Ef non è coinvolto in questo passaggio.
  3. Infine, DbContext.SaveChanges() viene chiamato . A questo punto, Entity Framework rileva automaticamente le modifiche confrontando le entità con uno snapshot dal momento in cui sono state caricate. Eventuali modifiche rilevate vengono mantenute nel database; quando si usa un database relazionale, questo comporta in genere l'invio di un'istanza di SQL UPDATE per aggiornare le righe pertinenti.

Si noti che nell'esempio precedente è stata descritta un'operazione di aggiornamento tipica per i dati esistenti, ma sono presenti principi simili per l'aggiunta e la rimozione di entità. È possibile interagire con lo strumento di rilevamento delle modifiche di Entity Framework chiamando DbSet<TEntity>.Add e Remove, causando il rilevamento delle modifiche. Entity Framework applica quindi tutte le modifiche rilevate al database quando SaveChanges() viene chiamato ,ad esempio tramite SQL INSERT e DELETE quando si usa un database relazionale.

SaveChanges() offre i vantaggi seguenti:

  • Non è necessario scrivere codice per tenere traccia delle entità e delle proprietà modificate. Ef esegue automaticamente questa operazione e aggiorna solo tali proprietà nel database, migliorando le prestazioni. Si supponga che le entità caricate siano associate a un componente dell'interfaccia utente, consentendo agli utenti di modificare qualsiasi proprietà desiderata; EF elimina il carico di capire quali entità e proprietà sono state effettivamente modificate.
  • Il salvataggio delle modifiche nel database può talvolta risultare complicato. Ad esempio, se si desidera aggiungere un blog e alcuni post per tale blog, potrebbe essere necessario recuperare la chiave generata dal database per il blog inserito prima di poter inserire i post (poiché devono fare riferimento al blog). EF esegue tutto questo per te, togliendo la complessità.
  • Entity Framework può rilevare problemi di concorrenza, ad esempio quando una riga di database è stata modificata da un altro utente tra la query e SaveChanges(). Altri dettagli sono disponibili nei conflitti di concorrenza.
  • Nei database che lo supportano, SaveChanges() esegue automaticamente il wrapping di più modifiche in una transazione, assicurando che i dati rimangano coerenti in caso di errore. Altri dettagli sono disponibili in Transazioni.
  • SaveChanges() raggruppa anche più modifiche in molti casi, riducendo significativamente il numero di round trip del database e migliorando notevolmente le prestazioni. Altri dettagli sono disponibili in Aggiornamento efficiente.

Per altre informazioni e esempi di codice sull'utilizzo di base SaveChanges() , vedere Basic SaveChanges. Per altre informazioni sul rilevamento delle modifiche di Entity Framework, vedere Panoramica del rilevamento delle modifiche.

Approccio 2: ExecuteUpdate e ExecuteDelete ("aggiornamento bulk")

Nota

Questa funzionalità è stata introdotta in EF Core 7.0.

Anche se il rilevamento delle modifiche e SaveChanges() sono un modo efficace per salvare le modifiche, presentano alcuni svantaggi.

Prima di tutto, SaveChanges() è necessario eseguire una query e tenere traccia di tutte le entità che verranno modificate o eliminate. Se è necessario, ad esempio, eliminare tutti i blog con una classificazione inferiore a una determinata soglia, è necessario eseguire query, materializzare e tenere traccia di un numero potenzialmente elevato di righe e generare SaveChanges() un'istruzione DELETE per ognuno di essi e ognuno di essi. I database relazionali offrono un'alternativa molto più efficiente: è possibile inviare un singolo DELETE comando, specificando le righe da eliminare tramite una WHERE clausola, ma il SaveChanges() modello non consente di generarlo.

Per supportare questo scenario di aggiornamento in blocco, è possibile usare ExecuteDelete come segue:

context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();

In questo modo è possibile esprimere un'istruzione SQL DELETE tramite normali operatori LINQ, simili a una normale query LINQ, causando l'esecuzione di SQL seguente nel database:

DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3

Questa operazione viene eseguita in modo molto efficiente nel database, senza caricare dati dal database o coinvolgere lo strumento di rilevamento delle modifiche di Entity Framework. Analogamente, ExecuteUpdate consente di esprimere un'istruzione SQL UPDATE .

Anche se non si modificano entità in blocco, è possibile sapere esattamente quali proprietà di quale entità si desidera modificare. L'uso dell'API di rilevamento delle modifiche per eseguire la modifica può essere eccessivamente complessa, richiedendo la creazione di un'istanza di entità, il rilevamento tramite Attach, l'esecuzione delle modifiche e infine la chiamata SaveChanges()a . Per questi scenari e ExecuteUpdate ExecuteDelete può essere un modo notevolmente più semplice per esprimere la stessa operazione.

Infine, sia il rilevamento delle modifiche che SaveChanges() se stesso impongono un determinato sovraccarico di runtime. Se si sta scrivendo un'applicazione ExecuteUpdate a prestazioni elevate e ExecuteDelete si consente di evitare sia questi componenti che generare in modo efficiente l'istruzione desiderata.

Si noti tuttavia che ExecuteUpdate e ExecuteDelete presentano anche alcune limitazioni:

  • Questi metodi vengono eseguiti immediatamente e attualmente non possono essere inseriti in batch con altre operazioni. D'altra parte, SaveChanges(), può raggruppare più operazioni.
  • Poiché il rilevamento delle modifiche non è coinvolto, è responsabilità dell'utente sapere esattamente quali entità e proprietà devono essere modificate. Questo può significare un codice più manuale e di basso livello che tiene traccia di ciò che deve cambiare e cosa non lo fa.
  • Inoltre, poiché il rilevamento delle modifiche non è coinvolto, questi metodi non applicano automaticamente il controllo della concorrenza quando si salvano in modo permanente le modifiche. Tuttavia, è comunque possibile aggiungere in modo esplicito una Where clausola per implementare manualmente il controllo della concorrenza.
  • Attualmente è supportato solo l'aggiornamento e l'eliminazione; l'inserimento deve essere eseguito tramite DbSet<TEntity>.Add e SaveChanges().

Per altre informazioni ed esempi di codice, vedere ExecuteUpdate e ExecuteDelete.

Riepilogo

Di seguito sono riportate alcune linee guida per quando usare l'approccio. Si noti che queste non sono regole assolute, ma forniscono un'utile regola generale:

  • Se non si sa in anticipo quali modifiche verranno apportate, usare SaveChanges; rileverà automaticamente quali modifiche devono essere applicate. Scenari di esempio:
    • "Si vuole caricare un blog dal database e visualizzare un modulo che consente all'utente di modificarlo"
  • Se è necessario modificare un grafo di oggetti (ad esempio più oggetti interconnessi), usare SaveChanges; verrà illustrato l'ordinamento corretto delle modifiche e come collegare tutto insieme.
    • "Voglio aggiornare un blog, modificando alcuni dei suoi post ed eliminando altri"
  • Se si vuole modificare un numero potenzialmente elevato di entità in base a un criterio, usare ExecuteUpdate e ExecuteDelete. Scenari di esempio:
    • "Voglio dare a tutti i dipendenti un aumento"
    • "Voglio eliminare tutti i blog il cui nome inizia con X"
  • Se si conosce già esattamente quali entità si desidera modificare e come modificarle, usare ExecuteUpdate e ExecuteDelete. Scenari di esempio:
    • "Voglio eliminare il blog il cui nome è 'Foo'"
    • "Voglio cambiare il nome del blog con ID 5 in 'Bar'"