Veri Çekirdeği Oluşturma

Veri tohumlama, bir veritabanını başlangıç veri kümesiyle doldurma işlemidir.

Bunun EF Core'da gerçekleştirilmesinin birkaç yolu vardır:

Yapılandırma seçenekleri UseSeeding ve UseAsyncSeeding yöntemleri

EF 9 UseSeeding , veritabanını ilk verilerle dağıtmanın kullanışlı bir yolunu sağlayan ve UseAsyncSeeding yöntemleri sunar. Bu yöntemler, özel başlatma mantığını kullanma deneyimini geliştirmeyi amaçlar (aşağıda açıklanmıştır). Tüm veri tohumlama kodunun yerleştirilebileceği tek bir açık konum sağlar. Ayrıca, içindeki kod UseSeeding ve UseAsyncSeeding yöntemleri eşzamanlılık sorunlarını önlemek için geçiş kilitleme mekanizması tarafından korunur.

Yeni tohumlama yöntemleri, Migrate model değişikliği olmasa ve hiçbir geçiş uygulanmasa bile işlemin ve dotnet ef database update komutun bir parçası EnsureCreated olarak çağrılır.

İpucu

UseSeeding VE UseAsyncSeeding kullanmak, EF Core ile çalışırken veritabanını ilk verilerle dağıtmanın önerilen yoludur.

Bu yöntemler, seçenekler yapılandırma adımında ayarlanabilir. Örnek aşağıda verilmiştir:

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

Not

UseSeeding yönteminden EnsureCreated çağrılır ve UseAsyncSeeding yönteminden çağrılır EnsureCreatedAsync . Bu özelliği kullanırken, EF kullanan kod zaman uyumsuz olsa bile benzer mantık kullanarak hem hem de UseSeeding UseAsyncSeeding yöntemlerinin uygulanması önerilir. EF Core araçları şu anda yöntemin zaman uyumlu sürümüne dayanır ve yöntem uygulanmazsa UseSeeding veritabanını doğru şekilde dağıtmaz.

Özel başlatma mantığı

Veri tohumlama gerçekleştirmenin basit ve güçlü bir yolu, ana uygulama mantığı yürütülmeye başlamadan önce kullanmaktır DbContext.SaveChanges() . Bu amaçla ve UseAsyncSeeding kullanılması UseSeeding önerilir, ancak bazen bu yöntemleri kullanmak iyi bir çözüm değildir. Örnek bir senaryo, dağıtım için tek bir işlemde iki farklı bağlam kullanılması gerekir. Uygulamada doğrudan özel başlatma gerçekleştiren bir kod örneği aşağıda verilmiştir:

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

}

Uyarı

Birden çok örnek çalışırken eşzamanlılık sorunlarına neden olabileceğinden ve uygulamanın veritabanı şemasını değiştirme iznine sahip olmasını gerektireeceğinden, tohumlama kodu normal uygulama yürütmesinin parçası olmamalıdır.

Dağıtımınızın kısıtlamalarına bağlı olarak başlatma kodu farklı şekillerde yürütülebilir:

  • Başlatma uygulamasını yerel olarak çalıştırma
  • Başlatma uygulamasını ana uygulamayla dağıtma, başlatma yordamını çağırma ve başlatma uygulamasını devre dışı bırakma veya kaldırma.

Bu genellikle yayımlama profilleri kullanılarak otomatikleştirilebilir.

Model yönetilen verileri

Veriler, model yapılandırmasının bir parçası olarak bir varlık türüyle de ilişkilendirilebilir. Ardından EF Core geçişleri , veritabanını modelin yeni bir sürümüne yükseltirken hangi ekleme, güncelleştirme veya silme işlemlerinin uygulanması gerektiğini otomatik olarak hesaplayabilir.

Uyarı

Geçişler yalnızca yönetilen verileri istenen duruma getirmek için hangi işlemin gerçekleştirilmesi gerektiğini belirlerken model değişikliklerini dikkate alır. Bu nedenle, geçişler dışında gerçekleştirilen verilerde yapılan değişiklikler kaybolabilir veya bir hataya neden olabilir.

Örneğin, bu, içindeki OnModelCreatingiçin Country yönetilen verileri yapılandıracaktır:

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

İlişkisi olan varlıkları eklemek için yabancı anahtar değerlerinin belirtilmesi gerekir:

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

Çoka çok gezintileri için verileri yönetirken birleştirme varlığının açıkça yapılandırılması gerekir. Varlık türünün gölge durumunda herhangi bir özelliği varsa (örneğin LanguageCountry , aşağıdaki birleştirme varlığı), değerleri sağlamak için anonim bir sınıf kullanılabilir:

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

Sahip olunan varlık türleri benzer şekilde yapılandırılabilir:

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

Daha fazla bağlam için örnek projenin tamamına bakın.

Veriler modele eklendikten sonra, değişiklikleri uygulamak için geçişler kullanılmalıdır.

İpucu

Geçişleri otomatik bir dağıtımın parçası olarak uygulamanız gerekiyorsa, yürütmeden önce önizlenebilen bir SQL betiği oluşturabilirsiniz.

Alternatif olarak, örneğin bir test veritabanı veya bellek içi sağlayıcıyı veya ilişkisel olmayan herhangi bir veritabanını kullanırken yönetilen verileri içeren yeni bir veritabanı oluşturmak için kullanabilirsiniz context.Database.EnsureCreated() . Veritabanı zaten varsa, EnsureCreated() veritabanındaki şemayı veya yönetilen verileri güncelleştirmez. geçişleri kullanmayı planlıyorsanız ilişkisel veritabanları için aramamanız EnsureCreated() gerekir.

Not

"Veri tohumlama" olarak adlandırılan yöntemi kullanarak HasData veritabanını doldurma. Bu adlandırma, özelliğin bir dizi sınırlaması olduğundan ve yalnızca belirli veri türleri için uygun olduğundan yanlış beklentileri ayarlar. Bu nedenle bunu "model yönetilen verileri" olarak yeniden adlandırmaya karar verdik. UseSeeding ve UseAsyncSeeding yöntemleri genel amaçlı veri tohumlama için kullanılmalıdır.

Model tarafından yönetilen verilerin sınırlamaları

Bu tür veriler geçişler tarafından yönetilir ve veritabanında zaten bulunan verilerin güncelleştirilmesi için betiğin veritabanına bağlanmadan oluşturulması gerekir. Bu, bazı kısıtlamalar uygular:

  • Birincil anahtar değeri genellikle veritabanı tarafından oluşturulmuş olsa bile belirtilmelidir. Geçişler arasındaki veri değişikliklerini algılamak için kullanılır.
  • Birincil anahtar herhangi bir şekilde değiştirilirse daha önce eklenen veriler kaldırılır.

Bu nedenle, bu özellik en çok geçişler dışında değişmesi beklenmeyen statik veriler için kullanışlıdır ve veritabanındaki posta kodları gibi başka hiçbir şeye bağımlı değildir.

Senaryonuz aşağıdakilerden birini içeriyorsa, ilk bölümde açıklanan ve UseAsyncSeeding yöntemlerini kullanmanız UseSeeding önerilir:

  • Test için geçici veriler
  • Veritabanı durumuna bağlı veriler
  • Büyük veriler (çekirdek oluşturma verileri geçiş anlık görüntülerinde yakalanır ve büyük veriler hızla büyük dosyalara ve düşük performansa yol açabilir).
  • Veritabanı tarafından anahtar değerlerinin oluşturulmasını gerektiren veriler( kimlik olarak alternatif anahtar kullanan varlıklar da dahil)
  • Bazı parola karması gibi özel dönüştürme gerektiren veriler (değer dönüştürmeleri tarafından işlenmez)
  • ASP.NET Çekirdek Kimlik rolleri ve kullanıcı oluşturma gibi dış API'ye çağrı gerektiren veriler

El ile geçiş özelleştirmesi

Geçiş eklendiğinde, ile HasData belirtilen verilerde yapılan değişiklikler , UpdateData()ve DeleteData()çağrılarına InsertData()dönüştürülür. Sınırlamalardan bazılarını geçici olarak gidermenin bir yolu, geçişe bu çağrıları HasData veya özel işlemleri el ile eklemektir.

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