Megosztás a következőn keresztül:


Adatelőkészítés

Az adatbevetés egy adatbázis kezdeti adatkészlettel való feltöltésének folyamata.

Ez az EF Core-ban többféleképpen is elvégezhető:

Konfigurációs beállítások UseSeeding és UseAsyncSeeding metódusok

Az EF 9 bevezette UseSeeding és UseAsyncSeeding módszereket, amelyek kényelmes módot biztosítanak az adatbázis kezdeti adatokkal való bevetésére. Ezek a módszerek az egyéni inicializálási logika használatának élményét hivatottak javítani (az alábbiakban ismertetjük). Egy egyértelmű helyet biztosítanak, ahol az összes adatbevetési kód elhelyezhető. Ezenkívül a UseSeeding és UseAsyncSeeding metódusok kódját a migrálási zárolási mechanizmus védi, az egyidejűségi problémák megelőzése érdekében.

Az új vetési módszereket EnsureCreated művelet, Migrate és dotnet ef database update parancs részeként hívjuk meg, még akkor is, ha nincsenek modellmódosítások, és nem alkalmaztak áttelepítést.

Borravaló

A UseSeeding és a UseAsyncSeeding használata az adatbázis kezdeti adatokkal való üzembe helyezésének ajánlott módja az EF Core használatakor.

Ezeket a metódusokat a beállítási lehetőségekkonfigurációs lépésében lehet beállítani. Íme egy példa:

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);
            }
        });

Jegyzet

A UseSeeding-t a EnsureCreated metódusból hívják meg, és a UseAsyncSeeding-t a EnsureCreatedAsync metódusból hívják meg. A funkció használatakor ajánlott UseSeeding és UseAsyncSeeding metódusokat is hasonló logikával implementálni, még akkor is, ha az EF-t használó kód aszinkron. Az EF Core eszközkészlet jelenleg a módszer szinkron verziójára támaszkodik, és nem fogja megfelelően kiépítani az adatbázist, ha a UseSeeding metódus nincs implementálva.

Egyéni inicializálási logika

Az adatekjesítés végrehajtásának egyszerű és hatékony módja, ha a DbContext.SaveChangesAsync()-t használja, mielőtt a fő alkalmazáslogika végrehajtása elkezdődik. Erre a célra ajánlott UseSeeding és UseAsyncSeeding használni, de néha ezek a módszerek nem jó megoldás. Példaforgatókönyv, ha a vetéshez két különböző kontextust kell használni egy tranzakcióban. Az alábbiakban egy kódminta látható, amely közvetlenül egyéni inicializálást hajt végre az alkalmazásban:

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

Figyelmeztetés

Az inicializáló kód nem lehet része a normál alkalmazásvégrehajtásnak, mivel ez egyidejűségi problémákat okozhat, ha több példány fut, és ehhez az alkalmazásnak jogosultsággal kell rendelkeznie az adatbázisséma módosításához.

Az üzembe helyezés korlátaitól függően az inicializálási kód többféleképpen is végrehajtható:

  • Az inicializálási alkalmazás helyi futtatása
  • Az inicializálási alkalmazás központi telepítése a főalkalmazással, az inicializálási rutin meghívása, az inicializálási alkalmazás letiltása vagy eltávolítása.

Ez általában közzétételi profilokhasználatával automatizálható.

Felügyelt adatok modellezése

Az adatok entitástípushoz is társíthatók a modellkonfiguráció részeként. Ezután az EF Core migrálások automatikusan kiszámítják, hogy milyen beszúrási, frissítési vagy törlési műveleteket kell alkalmazni az adatbázis új verziójára való frissítéskor.

Figyelmeztetés

A migrálások csak a modell módosításait veszi figyelembe annak meghatározásakor, hogy milyen műveletet kell végrehajtani a felügyelt adatok kívánt állapotba helyezéséhez. Így a migráláson kívül végrehajtott adatok bármilyen módosítása elveszhet, vagy hibát okozhat.

Például ez konfigurálja a felügyelet alá vont adatokat egy Country számára 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" });
});

Ha olyan entitásokat szeretne hozzáadni, amelyek kapcsolatban vannak, meg kell adni az idegenkulcs-értékeket:

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 });

A több-a-többhöz navigációk adatainak kezelésekor az illesztési entitást explicit módon kell konfigurálni. Ha az entitástípus rendelkezik rejtett állapotú tulajdonságokkal (pl. az alábbi LanguageCountry kapcsoló entitás esetében), névtelen osztállyal lehet értékeket adni.

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 });
            });
});

A saját entitástípusok hasonló módon konfigurálhatók:

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 });

További információért tekintse meg a teljes mintaprojekt.

Miután az adatokat hozzáadta a modellhez, a módosítások alkalmazásához migrációkat kell használni.

Borravaló

Ha egy automatizált üzembe helyezés részeként migrálásokat kell alkalmaznia, létrehozhat egy SQL-szkriptet, amely a végrehajtás előtt megtekinthető.

Alternatívaként a(z) context.Database.EnsureCreatedAsync() segítségével létrehozhat egy új adatbázist, amely kezelt adatokat tartalmaz, például egy tesztadatbázis számára, vagy a memóriabeli tároló, illetve bármely nem relációs adatbázis használatakor. Vegye figyelembe, hogy ha az adatbázis már létezik, EnsureCreatedAsync() nem frissíti a sémát és a felügyelt adatokat az adatbázisban. Relációs adatbázisok esetén nem szabad meghívni EnsureCreatedAsync(), ha migrálást tervez használni.

Jegyzet

Az adatbázis feltöltése az "adatbevetésnek" nevezett HasData módszerrel. Ez az elnevezés helytelen elvárásokat állít be, mivel a funkció számos korlátozással rendelkezik, és csak bizonyos adattípusokhoz megfelelő. Ezért döntöttünk úgy, hogy átnevezzük a "modell által felügyelt adatokra". UseSeeding és UseAsyncSeeding módszereket kell használni az általános célú adatbevetéshez.

A modell által felügyelt adatok korlátozásai

Ezt az adattípust migrálások kezelik, és az adatbázisban már meglévő adatok frissítéséhez szükséges szkriptet az adatbázishoz való csatlakozás nélkül kell létrehozni. Ez bizonyos korlátozásokat vezet be:

  • Az elsődleges kulcs értékét akkor is meg kell adni, ha azt általában az adatbázis hozza létre. A rendszer a migrálások közötti adatváltozások észlelésére szolgál.
  • A korábban beszúrt adatok törlődnek, ha az elsődleges kulcs bármilyen módon módosul.

Ezért ez a funkció leginkább olyan statikus adatok esetében hasznos, amelyek várhatóan nem változnak a migráláson kívül, és nem függ az adatbázisban lévő bármi mástól, például az irányítószámoktól.

Ha a forgatókönyv az alábbiak bármelyikét tartalmazza, ajánlott az első szakaszban leírt UseSeeding és UseAsyncSeeding metódusokat használni:

  • Ideiglenes adatok teszteléshez
  • Az adatbázis állapotától függő adatok
  • Nagy méretű adatok (a bevetési adatok a migrációs pillanatképekben kerülnek rögzítésre, és a nagy adatmennyiség gyorsan hatalmas fájlokhoz és teljesítményromláshoz vezethet).
  • Az adatbázis által generálandó kulcsértékeket igénylő adatok, beleértve azokat az entitásokat is, amelyek alternatív kulcsokat használnak identitásként
  • Egyéni átalakítást igénylő adatok (amelyeket nem értékkonvertálásokkezelnek), például jelszókivonatok
  • Külső API-ra irányuló hívásokat igénylő adatok, például ASP.NET Core Identity-szerepkörök és felhasználók létrehozása
  • Nem rögzített és determinisztikusan nem meghatározott adatok, például a DateTime.Nowiniciálása.

Manuális migrálás testreszabása

Migrálás hozzáadásakor a HasData-n megadott adatok változásai InsertData(), UpdateData()és DeleteData()hívássá alakulnak át. A HasData néhány korlátozásának megkerülésére az egyik módszer az, ha manuálisan hozzáadja ezeket a hívásokat, vagy egyéni műveleteket, a migráláshoz.

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 }
    });