Seeding dei dati
Il seeding dei dati è il processo di popolamento di un database con un set iniziale di dati.
È possibile eseguire questa operazione in EF Core in diversi modi:
- Dati di inizializzazione del modello
- Personalizzazione della migrazione manuale
- Logica di inizializzazione personalizzata
Dati di inizializzazione del modello
A differenza di EF6, in EF Core i dati di seeding possono essere associati a un tipo di entità come parte della configurazione del modello. Le migrazioni di EF Core possono quindi calcolare automaticamente le operazioni di inserimento, aggiornamento o eliminazione da applicare durante l'aggiornamento del database a una nuova versione del modello.
Nota
Le migrazioni considerano solo le modifiche del modello quando si determina l'operazione da eseguire per ottenere i dati di inizializzazione nello stato desiderato. Di conseguenza, eventuali modifiche apportate ai dati eseguiti all'esterno delle migrazioni potrebbero andare perse o causare un errore.
Ad esempio, in questo modo verranno configurati i dati di inizializzazione per un Blog
in OnModelCreating
:
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
Per aggiungere entità con una relazione, è necessario specificare i valori di chiave esterna:
modelBuilder.Entity<Post>().HasData(
new Post { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
Se il tipo di entità ha proprietà nello stato shadow, è possibile usare una classe anonima per fornire i valori:
modelBuilder.Entity<Post>().HasData(
new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });
I tipi di entità di proprietà possono essere sottoposto a seeding in modo simile:
modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
new { PostId = 1, First = "Andriy", Last = "Svyryd" },
new { PostId = 2, First = "Diego", Last = "Vega" });
Per altre informazioni di contesto, vedere il progetto di esempio completo.
Dopo aver aggiunto i dati al modello, è necessario usare le migrazioni per applicare le modifiche.
Suggerimento
Se è necessario applicare le migrazioni come parte di una distribuzione automatizzata, è possibile creare uno script SQL che può essere visualizzato in anteprima prima dell'esecuzione.
In alternativa, è possibile usare context.Database.EnsureCreated()
per creare un nuovo database contenente i dati di inizializzazione, ad esempio per un database di test o quando si usa il provider in memoria o qualsiasi database non relazionale. Si noti che se il database esiste già, EnsureCreated()
non aggiornerà né lo schema né i dati di inizializzazione nel database. Per i database relazionali non è consigliabile chiamare EnsureCreated()
se si prevede di usare le migrazioni.
Limitazioni dei dati di inizializzazione del modello
Questo tipo di dati di inizializzazione viene gestito dalle migrazioni e lo script per aggiornare i dati già presenti nel database deve essere generato senza connettersi al database. Ciò impone alcune restrizioni:
- Il valore della chiave primaria deve essere specificato anche se in genere viene generato dal database. Verrà usato per rilevare le modifiche ai dati tra le migrazioni.
- I dati con seeding in precedenza verranno rimossi se la chiave primaria viene modificata in qualsiasi modo.
Questa funzionalità è quindi più utile per i dati statici che non devono cambiare all'esterno delle migrazioni e non dipendono da altri elementi nel database, ad esempio i codici ZIP.
Se lo scenario include una delle opzioni seguenti, è consigliabile usare la logica di inizializzazione personalizzata descritta nell'ultima sezione:
- Dati temporanei per i test
- Dati che dipendono dallo stato del database
- Dati di grandi dimensioni (i dati di seeding vengono acquisiti negli snapshot di migrazione e i dati di grandi dimensioni possono portare rapidamente a file di grandi dimensioni e prestazioni ridotte).
- Dati che devono essere generati dal database con valori chiave, incluse le entità che usano chiavi alternative come identità
- Dati che richiedono una trasformazione personalizzata (non gestita dalle conversioni di valori), ad esempio alcuni hash delle password
- Dati che richiedono chiamate all'API esterna, ad esempio ASP.NET ruoli di identità di base e creazione di utenti
Personalizzazione della migrazione manuale
Quando viene aggiunta una migrazione, le modifiche ai dati specificati con HasData
vengono trasformate in chiamate a InsertData()
, UpdateData()
e DeleteData()
. Un modo per aggirare alcune delle limitazioni di HasData
consiste nell'aggiungere manualmente queste chiamate o operazioni personalizzate alla migrazione.
migrationBuilder.InsertData(
table: "Blogs",
columns: new[] { "Url" },
values: new object[] { "http://generated.com" });
Logica di inizializzazione personalizzata
Un modo semplice e potente per eseguire il seeding dei dati consiste nell'usare DbContext.SaveChanges()
prima che la logica principale dell'applicazione inizi l'esecuzione.
using (var context = new DataSeedingContext())
{
context.Database.EnsureCreated();
var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
}
context.SaveChanges();
}
Avviso
Il codice di seeding non deve far parte della normale esecuzione dell'app perché ciò può causare problemi di concorrenza quando sono in esecuzione più istanze e richiederebbe anche all'app l'autorizzazione per modificare lo schema del database.
A seconda dei vincoli della distribuzione, il codice di inizializzazione può essere eseguito in modi diversi:
- Esecuzione dell'app di inizializzazione in locale
- Distribuzione dell'app di inizializzazione con l'app principale, richiamando la routine di inizializzazione e disabilitando o rimuovendo l'app di inizializzazione.
Questo può in genere essere automatizzato usando i profili di pubblicazione.