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 veri tohumlama (
UseSeeding
) - Özel başlatma mantığı
- Model yönetilen verileri (
HasData
) - El ile geçiş özelleştirmesi
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 OnModelCreating
iç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 }
});