Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Seedování dat je proces naplnění databáze počáteční sadou dat.
V EF Core můžete dosáhnout několika způsoby:
-
Počáteční data možností konfigurace (
UseSeeding) - Logika vlastní inicializace
-
Modelem spravovaná data (
HasData) - Přizpůsobení ruční migrace
Možnosti konfigurace UseSeeding a metody UseAsyncSeeding
EF 9 uvedl UseSeeding a UseAsyncSeeding metody, které poskytují pohodlný způsob osazování databáze počátečními daty. Cílem těchto metod je zlepšit zkušenosti s používáním vlastní inicializační logiky (vysvětleno níže). Poskytují jedno jasné místo, kde může být veškerý kód pro plnění dat umístěn. Kromě toho je kód uvnitř metod UseSeeding a UseAsyncSeeding chráněn mechanismem uzamčení migrace , aby se zabránilo problémům se souběžností.
Nové metody počátečního nasazení EnsureCreated se volají jako součást operace Migrate a příkazu dotnet ef database update, i když nedošlo ke změnám modelu a nebyly aplikovány žádné migrace.
Návod
Použití UseSeeding a UseAsyncSeeding je doporučeným způsobem počátečního nastavení databáze při práci s EF Core.
Tyto metody je možné nastavit v kroku konfigurace možností. Zde je příklad:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFDataSeeding;Trusted_Connection=True;ConnectRetryCount=0")
.UseSeeding((context, _) =>
{
var testBlog = context.Set<Blog>().FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
context.SaveChanges();
}
})
.UseAsyncSeeding(async (context, _, cancellationToken) =>
{
var testBlog = await context.Set<Blog>().FirstOrDefaultAsync(b => b.Url == "http://test.com", cancellationToken);
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync(cancellationToken);
}
});
Poznámka:
UseSeeding
/
UseAsyncSeedingjsou vyvolány během EnsureCreated/EnsureCreatedAsync a po migraci (napříkladMigrateAsyncMigrate/dotnet ef database update , a sady migrace).
Nástroje a sady EF Core aktuálně spoléhají na synchronní delegáta, takže vždy implementujte UseSeeding , i když vaše aplikace obvykle používá asynchronní rozhraní API.
Chování nasazení
UseSeeding a UseAsyncSeeding spusťte pouze v případech, kdy EF Core provádí inicializaci databáze nebo operaci migrace. Podle toho zvolte mechanismus nasazení:
| Operation | Vyvolání delegáta počátečního nastavení |
|---|---|
EnsureCreated nebo Migrate |
UseSeeding |
EnsureCreatedAsync nebo MigrateAsync |
UseAsyncSeeding |
dotnet ef database update nebo Update-Database |
UseSeeding |
| Sada migrace | UseSeeding |
| Skript SQL spuštěný externím nástrojem SQL | None |
Pro automatizované nasazení, které se musí spustit UseSeeding, použijte sadu migrace nebo vyhrazený proces inicializace. Nástroje a sady EF Core vyvolávají synchronní delegáta, takže vždy implementujte UseSeeding , i když aplikace obvykle používá asynchronní rozhraní API. Skript SQL použijte při kontrole nebo provedení DBA a počáteční data jsou reprezentována místo toho operacemi migrace. Viz Použití migrací pro kompromisy.
Po downgradu migrace se také spustí počáteční nasazení. Pokud aplikace podporuje downgrad na migraci, která neobsahuje všechny tabulky používané kódem počátečního nasazení, zkontrolujte, jestli před dotazováním existuje požadované schéma. To je zvlášť důležité při vrácení všech migrací cílením 0.
Aplikace Aspire mohou koordinovat místní provádění migrace a publikovat sady migrace nebo skripty s integrací migrací Aspire EF Core.
Logika vlastní inicializace
Jednoduchým a výkonným způsobem, jak provádět inicializaci dat, je použít SaveChangesAsync před spuštěním hlavní logiky aplikace. Doporučuje se použít UseSeeding a UseAsyncSeeding pro tento účel, ale někdy použití těchto metod není dobrým řešením. Ukázkový scénář je, když zasetí vyžaduje použití dvou různých kontextů v jedné transakci. Níže je ukázka kódu, která provádí vlastní inicializaci přímo v aplikaci:
using (var context = new DataSeedingContext())
{
await context.Database.EnsureCreatedAsync();
var testBlog = await context.Blogs.FirstOrDefaultAsync(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync();
}
}
Varování
Počáteční kód by neměl být součástí normálního spuštění aplikace, protože to může způsobit problémy souběžnosti při spuštění více instancí a zároveň by vyžadovalo, aby aplikace měla oprávnění ke změně schématu databáze.
V závislosti na omezeních nasazení je možné inicializační kód spustit různými způsoby:
- Místní spuštění inicializační aplikace
- Nasazení inicializační aplikace s hlavní aplikací, vyvolání rutiny inicializace a zakázání nebo odebrání inicializační aplikace.
Obvykle se to dá automatizovat pomocí publikačních profilů.
Modelem spravovaná data
Data lze také přidružit k typu entity v rámci konfigurace modelu. Migrace EF Core potom smějí automaticky spočítat, které operace vložení, aktualizace nebo odstranění je nutné provést při upgradu databáze na novou verzi modelu.
Varování
Migrace zohledňuje změny modelu pouze při určování operace, která se má provést, aby se spravovaná data dostala do požadovaného stavu. Jakékoli změny dat provedených mimo migrace proto můžou být ztraceny nebo způsobit chybu.
Tímto příkladem se nakonfigurují spravovaná data Country pro OnModelCreating.
modelBuilder.Entity<Country>(b =>
{
b.Property(x => x.Name).IsRequired();
b.HasData(
new Country { CountryId = 1, Name = "USA" },
new Country { CountryId = 2, Name = "Canada" },
new Country { CountryId = 3, Name = "Mexico" });
});
Pokud chcete přidat entity, které mají relaci, je potřeba zadat hodnoty cizího klíče:
modelBuilder.Entity<City>().HasData(
new City { Id = 1, Name = "Seattle", LocatedInId = 1 },
new City { Id = 2, Name = "Vancouver", LocatedInId = 2 },
new City { Id = 3, Name = "Mexico City", LocatedInId = 3 },
new City { Id = 4, Name = "Puebla", LocatedInId = 3 });
Při správě dat pro navigace M:N je potřeba entitu spojení nakonfigurovat explicitně. Pokud má typ entity nějaké vlastnosti ve stínovém stavu (např. entitu LanguageCountry join níže), dá se k zadání hodnot použít anonymní třída:
modelBuilder.Entity<Language>(b =>
{
b.HasData(
new Language { Id = 1, Name = "English" },
new Language { Id = 2, Name = "French" },
new Language { Id = 3, Name = "Spanish" });
b.HasMany(x => x.UsedIn)
.WithMany(x => x.OfficialLanguages)
.UsingEntity(
"LanguageCountry",
r => r.HasOne(typeof(Country)).WithMany().HasForeignKey("CountryId").HasPrincipalKey(nameof(Country.CountryId)),
l => l.HasOne(typeof(Language)).WithMany().HasForeignKey("LanguageId").HasPrincipalKey(nameof(Language.Id)),
je =>
{
je.HasKey("LanguageId", "CountryId");
je.HasData(
new { LanguageId = 1, CountryId = 2 },
new { LanguageId = 2, CountryId = 2 },
new { LanguageId = 3, CountryId = 3 });
});
});
Typy vlastněných entit lze konfigurovat podobným způsobem:
modelBuilder.Entity<Language>().OwnsOne(p => p.Details).HasData(
new { LanguageId = 1, Phonetic = false, Tonal = false, PhonemesCount = 44 },
new { LanguageId = 2, Phonetic = false, Tonal = false, PhonemesCount = 36 },
new { LanguageId = 3, Phonetic = true, Tonal = false, PhonemesCount = 24 });
Další kontext najdete v úplném ukázkovém projektu .
Po přidání dat do modelu by se migrace měly použít k použití změn.
HasData změny se převedou na InsertData, UpdateDataa DeleteData operace při generování migrace. Volání Migrate nekontroluje aktuální HasData konfiguraci nezávisle. Po změně dat spravovaných modelem přidejte a nasaďte novou migraci.
Návod
Pro automatizované nasazení použijte sadu migrace. Skript SQL použijte, pokud je nutné před spuštěním zobrazit náhled nebo změnit.
Alternativně můžete použít EnsureCreatedAsync k vytvoření nové databáze obsahující spravovaná data, například pro testovací databázi nebo při použití in-memory zprostředkovatele nebo jakékoli nerelační databáze. Upozorňujeme, že pokud databáze již existuje, EnsureCreatedAsync neaktualizuje schéma ani spravovaná data v databázi. U relačních databází byste neměli volat EnsureCreatedAsync, pokud plánujete používat Migrace.
Poznámka:
Naplnění databáze metodou HasData, která se dříve označovala jako "data seeding". Toto pojmenování nastavuje nesprávná očekávání, protože funkce má řadu omezení a je vhodná pouze pro konkrétní typy dat. Proto jsme se rozhodli přejmenovat na "data řízená modelem". UseSeeding a UseAsyncSeeding metody by měly být použity pro obecné účely počátečního osazení dat.
Omezení modelem spravovaných dat
Tento typ dat spravuje migrace a skript pro aktualizaci dat, která jsou již v databázi, je potřeba vygenerovat bez připojení k databázi. To ukládá určitá omezení:
- Hodnotu primárního klíče je potřeba zadat, i když je obvykle vygenerovaná databází. Použije se k detekci změn dat mezi migracemi.
- Dříve vložená data budou odebrána, pokud se primární klíč změní jakýmkoli způsobem.
Tato funkce je proto nejužitečnější pro statická data, u která se neočekává změna mimo migrace a nezávisí na ničem jiném v databázi, například PSČ.
Pokud váš scénář obsahuje některou z následujících možností, doporučujeme použít UseSeeding a UseAsyncSeeding použít metody popsané v první části:
- Dočasná data pro testování
- Data, která závisí na stavu databáze
- Data, která jsou velká (počáteční data se zaznamenávají ve snímcích migrace a velké objemy dat můžou rychle vést k obrovským souborům a snížení výkonu).
- Data, která potřebují, aby databáze vygenerovala hodnoty klíčů, včetně entit, které jako identitu používají alternativní klíče
- Data, která vyžadují vlastní transformaci (která není zpracována pomocí převodů hodnot
), například některá hashování hesel - Data, která vyžadují volání externího rozhraní API, jako jsou role identit ASP.NET Core a vytváření uživatelů
- Data, která nejsou pevná a deterministická, například zasetí do
DateTime.Now.
Přizpůsobení procesu ruční migrace
Při přidání migrace se změny dat, která jsou specifikována s HasData, transformují na volání InsertData(), UpdateData() a DeleteData(). Jedním ze způsobů, jak obejít některá omezení HasData , je ruční přidání těchto volání nebo vlastních operací do migrace.
migrationBuilder.InsertData(
table: "Countries",
columns: new[] { "CountryId", "Name" },
values: new object[,]
{
{ 1, "USA" },
{ 2, "Canada" },
{ 3, "Mexico" }
});
migrationBuilder.InsertData(
table: "Languages",
columns: new[] { "Id", "Name", "Details_PhonemesCount", "Details_Phonetic", "Details_Tonal" },
values: new object[,]
{
{ 1, "English", 44, false, false },
{ 2, "French", 36, false, false },
{ 3, "Spanish", 24, true, false }
});
migrationBuilder.InsertData(
table: "Cites",
columns: new[] { "Id", "LocatedInId", "Name" },
values: new object[,]
{
{ 1, 1, "Seattle" },
{ 2, 2, "Vancouver" },
{ 3, 3, "Mexico City" },
{ 4, 3, "Puebla" }
});
migrationBuilder.InsertData(
table: "LanguageCountry",
columns: new[] { "CountryId", "LanguageId" },
values: new object[,]
{
{ 2, 1 },
{ 2, 2 },
{ 3, 3 }
});
Tyto operace jsou vhodné, když se hodnoty a klíče opraví při zápisu migrace. Dotazují se na aktuální stav databáze. Příklady transformací SQL specifických pro konkrétní poskytovatele najdete v operacích s daty v migracíchInsertDataUpdateDataDeleteData.