Yabancı Tuşlar ve Gezintileri Değiştirme
Yabancı tuşlara ve gezintilere genel bakış
Entity Framework Core (EF Core) modelindeki ilişkiler yabancı anahtarlar (FK) kullanılarak temsil edilir. FK, ilişkideki bağımlı veya alt varlık üzerindeki bir veya daha fazla özellik içerir. Bağımlı/alt öğedeki yabancı anahtar özelliklerinin değerleri, asıl/üst öğedeki alternatif veya birincil anahtar (PK) özelliklerinin değerleriyle eşleştiğinde, bu bağımlı/alt varlık belirli bir asıl/üst varlıkla ilişkilendirilir.
Yabancı anahtarlar, veritabanında ilişkileri depolamak ve işlemek için iyi bir yoldur, ancak uygulama kodunda birden çok ilgili varlıkla çalışırken çok kolay değildir. Bu nedenle çoğu EF Core modeli de FK gösterimi üzerinde "gezintileri" katmanlar. Gezintiler, yabancı anahtar değerlerini birincil veya alternatif anahtar değerleriyle eşleştirerek bulunan ilişkilendirmeleri yansıtan varlık örnekleri arasında C#/.NET başvuruları oluşturur.
Gezintiler ilişkinin her iki tarafında, yalnızca bir tarafında kullanılabilir veya hiç kullanılamaz, yalnızca FK özelliği bırakılabilir. FK özelliği bir gölge özellik haline getirilerek gizlenebilir. İlişkileri modelleme hakkında daha fazla bilgi için bkz. İlişkiler.
Bahşiş
Bu belgede varlık durumlarının ve EF Core değişiklik izlemenin temellerinin anlaşıldığı varsayılır. Bu konular hakkında daha fazla bilgi için EF Core'daki Değişiklik İzleme bakın.
Bahşiş
GitHub’dan örnek kodu indirerek bu belgedeki tüm kodları çalıştırabilir ve hataları ayıklayabilirsiniz.
Örnek model
Aşağıdaki model, aralarında ilişki bulunan dört varlık türü içerir. Koddaki açıklamalar hangi özelliklerin yabancı anahtarlar, birincil anahtarlar ve gezintiler olduğunu gösterir.
public class Blog
{
public int Id { get; set; } // Primary key
public string Name { get; set; }
public IList<Post> Posts { get; } = new List<Post>(); // Collection navigation
public BlogAssets Assets { get; set; } // Reference navigation
}
public class BlogAssets
{
public int Id { get; set; } // Primary key
public byte[] Banner { get; set; }
public int? BlogId { get; set; } // Foreign key
public Blog Blog { get; set; } // Reference navigation
}
public class Post
{
public int Id { get; set; } // Primary key
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; } // Foreign key
public Blog Blog { get; set; } // Reference navigation
public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation
}
public class Tag
{
public int Id { get; set; } // Primary key
public string Text { get; set; }
public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation
}
Bu modeldeki üç ilişki şunlardır:
- Her blogda birçok gönderi (bire çok) olabilir:
Blog
asıl/üst öğedir.Post
bağımlı/alt öğedir. değerinin ilgili blogun PK değeriyle eşleşmesiBlog.Id
gereken FK özelliğiniPost.BlogId
içerir.Post.Blog
, bir gönderiden ilişkili bloga başvuru gezintisidir.Post.Blog
, içinBlog.Posts
ters gezintidir.Blog.Posts
, blogdan ilişkili tüm gönderilere bir koleksiyon gezintisidir.Blog.Posts
, içinPost.Blog
ters gezintidir.
- Her blogda bir varlık (bire bir) bulunabilir:
Blog
asıl/üst öğedir.BlogAssets
bağımlı/alt öğedir. değerinin ilgili blogun PK değeriyle eşleşmesiBlog.Id
gereken FK özelliğiniBlogAssets.BlogId
içerir.BlogAssets.Blog
, varlıklardan ilişkili bloga bir başvuru gezintisidir.BlogAssets.Blog
, içinBlog.Assets
ters gezintidir.Blog.Assets
, blogdan ilişkili varlıklara yönelik bir başvuru gezintisidir.Blog.Assets
, içinBlogAssets.Blog
ters gezintidir.
- Her gönderinin birçok etiketi olabilir ve her etiketin birçok gönderisi olabilir (çoka çok):
- Çoka çok ilişkileri, iki bire çok ilişkisine göre daha fazla katmandır. Çoka çok ilişkileri bu belgenin ilerleyen bölümlerinde ele alınmıştır.
Post.Tags
, gönderiden ilişkili tüm etiketlere bir koleksiyon gezintisidir.Post.Tags
, içinTag.Posts
ters gezintidir.Tag.Posts
etiketten ilişkili tüm gönderilere bir koleksiyon gezintisidir.Tag.Posts
, içinPost.Tags
ters gezintidir.
İlişkileri modelleme ve yapılandırma hakkında daha fazla bilgi için bkz. İlişkiler.
İlişki düzeltme
EF Core, gezintileri yabancı anahtar değerleriyle aynı hizada tutar ve tam tersi de geçerlidir. Başka bir ifadeyle, yabancı anahtar değeri artık farklı bir asıl/üst varlığa başvuruda bulunacak şekilde değişirse, gezintiler bu değişikliği yansıtacak şekilde güncelleştirilir. Benzer şekilde, bir gezinti değiştirilirse, ilgili varlıkların yabancı anahtar değerleri bu değişikliği yansıtacak şekilde güncelleştirilir. Buna "ilişki düzeltme" adı verilir.
Sorguya göre düzeltme
Düzeltme ilk olarak varlıklar veritabanından sorgulandığında gerçekleşir. Veritabanında yalnızca yabancı anahtar değerleri vardır, bu nedenle EF Core veritabanından bir varlık örneği oluşturduğunda, başvuru gezintilerini ayarlamak ve koleksiyon gezintilerine uygun varlıkları eklemek için yabancı anahtar değerlerini kullanır. Örneğin, bloglar ve ilişkili gönderileri ve varlıkları için bir sorgu düşünün:
using var context = new BlogsContext();
var blogs = context.Blogs
.Include(e => e.Posts)
.Include(e => e.Assets)
.ToList();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Her blog için EF Core önce bir Blog
örnek oluşturur. Ardından, her gönderi veritabanından Post.Blog
yüklendiğinden, başvuru gezintisi ilişkili bloga işaret etmek üzere ayarlanır. Benzer şekilde, gönderi koleksiyon gezintisine Blog.Posts
eklenir. ile aynı şey olur BlogAssets
, ancak bu durumda her iki gezinti de başvuru olur. Gezinti Blog.Assets
, varlık örneğine BlogAsserts.Blog
işaret etmek için, gezinti ise blog örneğine işaret etmek üzere ayarlanır.
Bu sorgudan sonra değişiklik izleyicisi hata ayıklama görünümüne baktığımızda her biri bir varlık ve iki gönderinin izlendiği iki blog gösterilir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: [{Id: 1}, {Id: 2}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []
Hata ayıklama görünümü hem anahtar değerlerini hem de gezintileri gösterir. Gezintiler, ilgili varlıkların birincil anahtar değerleri kullanılarak gösterilir. Örneğin, yukarıdaki çıktıda koleksiyon Posts: [{Id: 1}, {Id: 2}]
gezintisinin Blog.Posts
sırasıyla birincil anahtar 1 ve 2 ile ilişkili iki gönderi içerdiğini gösterir. Benzer şekilde, ilk blogla ilişkili her gönderi için satır, Blog: {Id: 1}
gezintinin Post.Blog
birincil anahtar 1 ile Blog'a başvurduğunu gösterir.
Yerel olarak izlenen varlıklara düzeltme
İlişki düzeltmesi, bir izleme sorgusundan döndürülen varlıklar ile DbContext tarafından izlenen varlıklar arasında da gerçekleşir. Örneğin bloglar, gönderiler ve varlıklar için üç ayrı sorgu yürütmeyi göz önünde bulundurun:
using var context = new BlogsContext();
var blogs = context.Blogs.ToList();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
var assets = context.Assets.ToList();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
var posts = context.Posts.ToList();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Hata ayıklama görünümlerine yeniden baktığımızda, ilk sorgudan sonra yalnızca iki blog izlenir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: []
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: <null>
Posts: []
Başvuru Blog.Assets
gezintileri null ve Blog.Posts
koleksiyon gezintileri boştur çünkü şu anda bağlam tarafından izlenen hiçbir ilişkili varlık yoktur.
İkinci sorgudan sonra, Blogs.Assets
başvuru gezintileri yeni izlenen BlogAsset
örnekleri işaret edene kadar düzeltildi. Benzer şekilde, BlogAssets.Blog
başvuru gezintileri de zaten izlenen Blog
uygun örneğe işaret etmek üzere ayarlanır.
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: []
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: []
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Son olarak, üçüncü sorgudan sonra koleksiyon Blog.Posts
gezintileri artık tüm ilgili gönderileri içerir ve Post.Blog
başvurular uygun Blog
örneğe işaret eder:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: [{Id: 1}, {Id: 2}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []
Bu, özgün tek sorguda elde edilen son durumla aynıdır çünkü EF Core, birden çok farklı sorgudan geldiğinde bile varlıklar izlendiğinden gezintileri düzeltir.
Dekont
Düzeltme hiçbir zaman veritabanından daha fazla veri döndürülür. Yalnızca sorgu tarafından zaten döndürülen veya DbContext tarafından zaten izlenen varlıkları bağlar. Varlıkları seri hale getirme sırasında yinelenenleri işleme hakkında bilgi için bkz . EF Core'da Kimlik Çözümlemesi.
Gezintileri kullanarak ilişkileri değiştirme
İki varlık arasındaki ilişkiyi değiştirmenin en kolay yolu, bir gezintiyi düzenlerken EF Core'dan ayrılarak ters gezinti ve FK değerlerini uygun şekilde düzeltmektir. Bunu aşağıdaki yöntemlerle yapabilirsiniz:
- Koleksiyon gezintisinde varlık ekleme veya kaldırma.
- Başvuru gezintisini farklı bir varlığa işaret etmek için değiştirme veya null olarak ayarlama.
Koleksiyon gezintilerini ekleme veya kaldırma
Örneğin, Visual Studio blogundaki gönderilerden birini .NET blog'a taşıyalım. Bunun için önce blogların ve gönderilerin yüklenmesi ve ardından gönderinin bir blogdaki gezinti koleksiyonundan diğer blogdaki gezinti koleksiyonuna taşınması gerekir:
using var context = new BlogsContext();
var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog");
var vsBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == "Visual Studio Blog");
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
vsBlog.Posts.Remove(post);
dotNetBlog.Posts.Add(post);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
context.SaveChanges();
Bahşiş
Hata ayıklama görünümüne ChangeTracker.DetectChanges() erişmek değişikliklerin otomatik olarak algılanmasına neden olmadığından çağrısı burada gereklidir.
Bu, yukarıdaki kodu çalıştırdıktan sonra yazdırılan hata ayıklama görünümüdür:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}, {Id: 2}, {Id: 3}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: <null>
Posts: [{Id: 4}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Modified
Id: 3 PK
BlogId: 1 FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 1}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []
Blog.Posts
.NET Blogu'nda gezintide artık üç gönderi (Posts: [{Id: 1}, {Id: 2}, {Id: 3}]
) vardır. Benzer şekilde, Blog.Posts
Visual Studio blogundaki gezintide de yalnızca bir gönderi (Posts: [{Id: 4}]
) vardır. Kod bu koleksiyonları açıkça değiştirdiğinden bu beklenen bir durumdur.
Daha da ilginç olan, kod gezintiyi açıkça değiştirmese Post.Blog
de Visual Studio blogunu (Blog: {Id: 1}
) işaret edecek şekilde düzeltilmiştir. Ayrıca, Post.BlogId
yabancı anahtar değeri .NET blogunun birincil anahtar değeriyle eşleşecek şekilde güncelleştirildi. Içindeki FK değerinde yapılan bu değişiklik, SaveChanges çağrıldığında veritabanında kalıcı hale getirmek için:
-- Executed DbCommand (0ms) [Parameters=[@p1='3' (DbType = String), @p0='1' (Nullable = true) (DbType = String)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
Başvuru gezintilerini değiştirme
Önceki örnekte, her blogdaki gönderilerin koleksiyon gezintisi değiştirilerek bir gönderi bir blogdan diğerine taşındı. Aynı şey, başvuru gezintisini yeni bloga Post.Blog
işaret eden şekilde değiştirerek de elde edilebilir. Örnek:
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
post.Blog = dotNetBlog;
Bu değişiklik sonrasındaki hata ayıklama görünümü, önceki örnektekiyle tamamen aynıdır . Bunun nedeni EF Core'un başvuru gezinti değişikliğini algılayıp koleksiyon gezintilerini ve FK değerini eşleşecek şekilde düzeltmesidir.
Yabancı anahtar değerlerini kullanarak ilişkileri değiştirme
Önceki bölümde ilişkiler, yabancı anahtar değerlerinin otomatik olarak güncelleştirilecek şekilde bırakılmasına neden olan gezintiler tarafından işleniyordu. EF Core'da ilişkileri işlemenin önerilen yolu budur. Ancak, FK değerlerini doğrudan işlemek de mümkündür. Örneğin, yabancı anahtar değerini değiştirerek bir gönderiyi Post.BlogId
bir blogdan diğerine taşıyabiliriz:
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
post.BlogId = dotNetBlog.Id;
Bunun, önceki örnekte gösterildiği gibi başvuru gezintisini değiştirmeye çok benzer olduğuna dikkat edin.
Bu değişiklik sonrasındaki hata ayıklama görünümü, önceki iki örnekte olduğu gibi yine tam olarak aynıdır . Bunun nedeni EF Core'un FK değer değişikliğini algılayıp hem başvuru hem de koleksiyon gezintilerini eşleşecek şekilde düzeltmesidir.
Bahşiş
bir ilişki her değiştiğinde tüm gezintileri ve FK değerlerini işlemek için kod yazmayın. Bu tür kod daha karmaşıktır ve her durumda yabancı anahtarlarda ve gezintilerde tutarlı değişiklikler olmasını sağlamalıdır. Mümkünse tek bir gezintiyi veya her iki gezintiyi de işlemeniz yeterlidir. Gerekirse FK değerlerini işlemeye devam edin. Hem gezintileri hem de FK değerlerini düzenlemekten kaçının.
Eklenen veya silinen varlıklar için düzeltme
Koleksiyon gezintisine ekleme
EF Core, koleksiyon gezintisine yeni bir bağımlı/alt varlık eklendiğini algıladığında aşağıdaki eylemleri gerçekleştirir:
- Varlık izlenmiyorsa izlenir. (Varlık genellikle durumunda olur
Added
. Ancak varlık türü oluşturulan anahtarları kullanacak şekilde yapılandırılmışsa ve birincil anahtar değeri ayarlanmışsa varlık durumunda izlenirUnchanged
.) - Varlık farklı bir sorumlu/üst öğeyle ilişkiliyse, bu ilişki kesilir.
- Varlık, koleksiyon gezintisine sahip olan asıl/üst öğeyle ilişkilendirildi.
- Gezintiler ve yabancı anahtar değerleri, ilgili tüm varlıklar için düzeltilir.
Buna dayanarak, bir gönderiyi bir blogdan diğerine taşımak için yenisine eklemeden önce eski koleksiyon gezintisinden kaldırmamıza gerek olmadığını görebiliriz. Bu nedenle yukarıdaki örnekteki kod şu şekilde değiştirilebilir:
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
vsBlog.Posts.Remove(post);
dotNetBlog.Posts.Add(post);
Hedef:
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
dotNetBlog.Posts.Add(post);
EF Core, gönderinin yeni bir bloga eklendiğini görür ve ilk blogdaki koleksiyondan otomatik olarak kaldırır.
Koleksiyon gezintisinden kaldırma
Bağımlı/alt varlığın asıl/üst öğesinin koleksiyon gezintisinden kaldırılması, ilgili sorumlu/üst öğeyle ilişkinin kesilmesine neden olur. Bundan sonra ne olacağı ilişkinin isteğe bağlı mı yoksa gerekli mi olduğuna bağlıdır.
İsteğe bağlı ilişkiler
İsteğe bağlı ilişkiler için varsayılan olarak yabancı anahtar değeri null olarak ayarlanır. Bu, bağımlı/alt öğesinin artık hiçbir sorumlu/üst öğeyle ilişkilendirilmemiş olduğu anlamına gelir. Örneğin, bir blog ve gönderi yükleyelim ve ardından gönderilerden birini koleksiyon gezintisinden Blog.Posts
kaldıralım:
var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5");
dotNetBlog.Posts.Remove(post);
Bu değişiklik sonrasında değişiklik izleme hata ayıklama görünümüne bakmak şunları gösterir:
Post.BlogId
FK null (BlogId: <null> FK Modified Originally 1
) olarak ayarlandı- Başvuru
Post.Blog
gezintisi null (Blog: <null>
) olarak ayarlandı - Gönderi koleksiyon gezintisinden
Blog.Posts
kaldırıldı (Posts: [{Id: 1}]
)
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Modified
Id: 2 PK
BlogId: <null> FK Modified Originally 1
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Tags: []
Gönderinin olarak Deleted
işaretlenmediğini fark edin. SaveChanges çağrıldığında veritabanındaki FK değerinin null olarak ayarlanması için olarak Modified
işaretlenir.
Gerekli ilişkiler
Gerekli ilişkiler için FK değerini null olarak ayarlamaya izin verilmez (ve genellikle mümkün değildir). Bu nedenle, gerekli bir ilişkinin kesilmesi, bağımlı/alt varlığın yeni bir sorumluya/üst öğeye yeniden üst öğe olarak atanması veya başvuru kısıtlama ihlalini önlemek için SaveChanges çağrıldığında veritabanından kaldırılması gerektiği anlamına gelir. Bu, "yalnız bırakılmışları silme" olarak bilinir ve EF Core'da gerekli ilişkiler için varsayılan davranıştır.
Örneğin, blog ile gönderiler arasındaki ilişkiyi gerekli olacak şekilde değiştirelim ve ardından önceki örnektekiyle aynı kodu çalıştıralım:
var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5");
dotNetBlog.Posts.Remove(post);
Bu değişiklik sonrasında hata ayıklama görünümüne bakmak şunları gösterir:
- Gönderi, SaveChanges çağrıldığında veritabanından silinecek şekilde
Deleted
işaretlendi. - Başvuru
Post.Blog
gezintisi null (Blog: <null>
olarak ayarlandı). - Gönderi koleksiyon gezintisinden
Blog.Posts
(Posts: [{Id: 1}]
) kaldırıldı.
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Tags: []
Post.BlogId
Gerekli bir ilişki için null olarak ayarlanamadığından değerinin değişmeden kaldığına dikkat edin.
SaveChanges çağrısı, yalnız bırakılmış gönderinin silinmesiyle sonuçlanır:
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
Yalnız bırakılmışların zamanlamasını ve yeniden üst öğeyi silme
Varsayılan olarak, yalnız bırakılmışları olarak Deleted
işaretleme, ilişki değişikliği algılanır algılanmaz gerçekleşir. Ancak, SaveChanges gerçekten çağrılana kadar bu işlem geciktirilebilir. Bu, bir sorumludan/üst öğeden kaldırılmış ancak SaveChanges çağrılmadan önce yeni bir sorumlu/üst öğeyle yeniden üst öğeye sahip olacak varlıkların yalnız bırakmasını önlemek için yararlı olabilir. ChangeTracker.DeleteOrphansTiming bu zamanlamayı ayarlamak için kullanılır. Örnek:
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
var post = vsBlog.Posts.Single(e => e.Title.StartsWith("Disassembly improvements"));
vsBlog.Posts.Remove(post);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
dotNetBlog.Posts.Add(post);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
context.SaveChanges();
gönderi ilk koleksiyondan kaldırıldıktan sonra nesne önceki örnekte olduğu gibi Deleted
işaretlenmez. Bunun yerine EF Core, bu gerekli bir ilişki olsa bile ilişkinin kesildiğini izliyor. (FK değeri, ef core tarafından null olarak kabul edilir, ancak türü null değer atanamaz. Bu, "kavramsal null" olarak bilinir.)
Post {Id: 3} Modified
Id: 3 PK
BlogId: <null> FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: []
Şu anda SaveChanges çağrısı, yalnız bırakılmış gönderinin silinmesine neden olur. Ancak yukarıdaki örnekte olduğu gibi, SaveChanges çağrılmadan önce gönderi yeni bir blogla ilişkilendirilirse, bu yeni bloga uygun şekilde düzeltilir ve artık yalnız bırakılmış olarak kabul edilmez:
Post {Id: 3} Modified
Id: 3 PK
BlogId: 1 FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 1}
Tags: []
Bu noktada çağrılan SaveChanges, veritabanındaki gönderiyi silmek yerine güncelleştirir.
Yalnız bırakılmışların otomatik olarak silinmesini kapatmak da mümkündür. Bir yalnız bırakılmış öğe izlenirken SaveChanges çağrılırsa bu bir özel durumla sonuçlanır. Örneğin, bu kod:
var dotNetBlog = context.Blogs.Include(e => e.Posts).Single(e => e.Name == ".NET Blog");
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.Never;
var post = dotNetBlog.Posts.Single(e => e.Title == "Announcing F# 5");
dotNetBlog.Posts.Remove(post);
context.SaveChanges(); // Throws
Şu özel durumu oluşturur:
System.InvalidOperationException:'{BlogId: 1}' anahtar değeriyle 'Blog' ve 'Post' varlıkları arasındaki ilişki koptu, ancak ilişki gerekli olarak işaretlendi veya yabancı anahtar null atanamaz olduğundan örtük olarak gerekli. Gerekli bir ilişki kesildiğinde bağımlı/alt varlığın silinmesi gerekiyorsa, ilişkiyi art arda silmeleri kullanacak şekilde yapılandırın.
Artıkların silinmesi ve art arda silme işlemleri istendiği zaman çağrılarak ChangeTracker.CascadeChanges()zorlanabilir. Bunu artık silme zamanlamasını Never
ayarlamakla birleştirdiğinizde, EF Core açıkça yönerge almadığı sürece yalnız bırakılmış öğeler hiçbir zaman silinmez.
Başvuru gezintisini değiştirme
Bire çok ilişkinin başvuru gezintisini değiştirmek, ilişkinin diğer ucundaki koleksiyon gezintisini değiştirmekle aynı etkiye sahiptir. Bağımlı/alt öğesinin başvuru gezintisini null olarak ayarlamak, varlığı asıl/üst öğe koleksiyonu gezintisinden kaldırmaya eşdeğerdir. Tüm düzeltme ve veritabanı değişiklikleri, ilişki gerekiyorsa varlığı yalnız bırakılmış hale getirmek de dahil olmak üzere önceki bölümde açıklandığı gibi gerçekleşir.
İsteğe bağlı bire bir ilişkiler
Bire bir ilişkilerde, başvuru gezintisini değiştirmek önceki tüm ilişkilerin kesilmesine neden olur. İsteğe bağlı ilişkiler için bu, daha önce ilişkili bağımlı/alt öğedeki FK değerinin null olarak ayarlandığı anlamına gelir. Örnek:
using var context = new BlogsContext();
var dotNetBlog = context.Blogs.Include(e => e.Assets).Single(e => e.Name == ".NET Blog");
dotNetBlog.Assets = new BlogAssets();
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
context.SaveChanges();
SaveChanges çağrılmadan önceki hata ayıklama görünümü, yeni varlıkların mevcut varlıkların yerini aldığı ve artık null BlogAssets.BlogId
bir FK değeriyle işaretlenmiş Modified
olduğunu gösterir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: -2147482629}
Posts: []
BlogAssets {Id: -2147482629} Added
Id: -2147482629 PK Temporary
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 1} Modified
Id: 1 PK
Banner: <null>
BlogId: <null> FK Modified Originally 1
Blog: <null>
Bu, SaveChanges çağrıldığında bir güncelleştirme ve ekleme ile sonuçlanır:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Assets" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p2=NULL, @p3='1' (Nullable = true) (DbType = String)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Assets" ("Banner", "BlogId")
VALUES (@p2, @p3);
SELECT "Id"
FROM "Assets"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Gerekli bire bir ilişkiler
Önceki örnektekiyle aynı kodu çalıştırmak, ancak bu kez gerekli bire bir ilişkiyle, yeni BlogAssets
yerine geçtiğinde yalnız bırakılmış duruma geldiğinden, daha önce ilişkilendirilmiş BlogAssets
olanın olarak Deleted
işaretlendiğini gösterir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: -2147482639}
Posts: []
BlogAssets {Id: -2147482639} Added
Id: -2147482639 PK Temporary
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 1} Deleted
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: <null>
Ardından SaveChanges çağrıldığında silme ve ekleme işlemiyle sonuçlanır:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Assets"
WHERE "Id" = @p0;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p1=NULL, @p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Assets" ("Banner", "BlogId")
VALUES (@p1, @p2);
SELECT "Id"
FROM "Assets"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Artıkları silinmiş olarak işaretleme zamanlaması, koleksiyon gezintilerinde gösterildiği gibi değiştirilebilir ve aynı etkilere sahiptir.
Varlık silme
İsteğe bağlı ilişkiler
Bir varlık olarak Deleted
işaretlendiğinde, örneğin çağrısı DbContext.Removeyapıldığında, silinen varlığa yapılan başvurular diğer varlıkların gezintilerinden kaldırılır. İsteğe bağlı ilişkiler için, bağımlı varlıklardaki FK değerleri null olarak ayarlanır.
Örneğin, Visual Studio blogunu olarak Deleted
işaretleyelim:
using var context = new BlogsContext();
var vsBlog = context.Blogs
.Include(e => e.Posts)
.Include(e => e.Assets)
.Single(e => e.Name == "Visual Studio Blog");
context.Remove(vsBlog);
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
context.SaveChanges();
SaveChanges çağrılmadan önce değişiklik izleyicisi hata ayıklama görünümüne bakmak şunları gösterir:
Blog {Id: 2} Deleted
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 2} Modified
Id: 2 PK
Banner: <null>
BlogId: <null> FK Modified Originally 2
Blog: <null>
Post {Id: 3} Modified
Id: 3 PK
BlogId: <null> FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: []
Post {Id: 4} Modified
Id: 4 PK
BlogId: <null> FK Modified Originally 2
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: <null>
Tags: []
Şu noktalara dikkat edin:
- Blog
Deleted
olarak işaretlendi. - Silinen blogla ilgili varlıklar null bir FK değerine (
BlogId: <null> FK Modified Originally 2
) ve null başvuru gezintisine (Blog: <null>
) sahip - Silinen blogla ilgili her gönderinin bir null FK değeri (
BlogId: <null> FK Modified Originally 2
) ve null başvuru gezintisi (Blog: <null>
) vardır
Gerekli ilişkiler
Gerekli ilişkiler için düzeltme davranışı isteğe bağlı ilişkilerle aynıdır, ancak bağımlı/alt varlıklar bir sorumlu/üst öğe olmadan var olamazlar olarak Deleted
işaretlenir ve başvuru kısıtlaması özel durumunu önlemek için SaveChanges çağrıldığında veritabanından kaldırılmalıdır. Bu, "art arda silme" olarak bilinir ve EF Core'da gerekli ilişkiler için varsayılan davranıştır. Örneğin, önceki örnekte olduğu gibi ancak gerekli bir ilişkiyle aynı kodun çalıştırılması SaveChanges çağrılmadan önce aşağıdaki hata ayıklama görünümünde sonuçlanır:
Blog {Id: 2} Deleted
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 2} Deleted
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 3} Deleted
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Deleted
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []
Beklendiği gibi, bağımlılar/alt öğeler olarak işaretlenir Deleted
. Ancak, silinen varlıklardaki gezintilerin değişmediğine dikkat edin. Bu garip görünebilir, ancak tüm gezintileri temizleyerek varlıkların silinmiş grafiğini tamamen parçalamaktan kaçınıyor. Başka bir ifadeyle, blog, varlık ve gönderiler silindikten sonra bile varlıkların grafiğini oluşturur. Bu, ef6'da grafiğin parçalandığı durumdan çok daha kolay bir varlık grafiğinin silinmesini kolaylaştırır.
Silme zamanlamasını art arda silme ve yeniden üst öğe oluşturma
Varsayılan olarak, üst/sorumlu olarak Deleted
işaretlenir işaretlenmez art arda silme gerçekleşir. Bu, daha önce açıklandığı gibi yalnız bırakılmışları silme işlemiyle aynıdır. Artıkları silerken olduğu gibi, bu işlem de SaveChanges çağrılana kadar gecikebilir, hatta uygun şekilde ayarlanarak ChangeTracker.CascadeDeleteTiming tamamen devre dışı bırakılabilir. Bu, bir asıl/üst öğe silindikten sonra alt öğeleri/bağımlıları yeniden üst öğelendirme de dahil olmak üzere yalnız bırakılmışları silme işlemiyle aynı şekilde yararlıdır.
Artıkları art arda silmenin yanı sıra artıkları silme işlemi, herhangi bir zamanda çağrılarak ChangeTracker.CascadeChanges()zorlanabilir. Bunu art arda silme zamanlamasını Never
olarak ayarlamakla birleştirmek, EF Core'a açıkça bunu yapması belirtilmediği sürece art arda silmelerin hiçbir zaman gerçekleşmemesini sağlar.
Bahşiş
Artıkları art arda silme ve silme işlemleri yakından ilişkilidir. Her ikisi de gerekli sorumlu/üst öğeyle ilişki kesildiğinde bağımlı/alt varlıkların silinmesiyle sonuçlanır. Art arda silme için bu kesme işlemi, asıl/üst öğe silindiği için gerçekleşir. Yalnız bırakılmışlar için, asıl/üst varlık hala var, ancak artık bağımlı/alt varlıklarla ilişkili değil.
Çoka çok ilişkiler
EF Core'da çoka çok ilişkiler birleştirme varlığı kullanılarak uygulanır. Çoka çok ilişkisinin her tarafı, bire çok ilişkisi olan bu birleşim varlığıyla ilişkilidir. Bu birleştirme varlığı açıkça tanımlanabilir ve eşlenebilir veya örtük olarak oluşturulup gizlenebilir. Her iki durumda da temel alınan davranış aynıdır. Çoka çok ilişkileri izlemenin nasıl çalıştığını anlamak için öncelikle bu temel davranışı inceleyeceğiz.
Kaça çok ilişkiler çalışır?
Açıkça tanımlanmış birleştirme varlık türünü kullanarak gönderiler ve etiketler arasında çoka çok ilişki oluşturan bu EF Core modelini göz önünde bulundurun:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; }
public Blog Blog { get; set; }
public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}
public class Tag
{
public int Id { get; set; }
public string Text { get; set; }
public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag
public Post Post { get; set; } // Reference navigation
public Tag Tag { get; set; } // Reference navigation
}
PostTag
Birleştirme varlık türünün iki yabancı anahtar özelliği içerdiğine dikkat edin. Bu modelde, bir gönderinin etiketle ilişkili olması için, yabancı anahtar değerinin PostTag.PostId
birincil anahtar değeriyle eşleştiği ve yabancı anahtar değerinin Post.Id
birincil anahtar değeriyle Tag.Id
eşleştiği PostTag.TagId
postTag birleştirme varlığı olmalıdır. Örnek:
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
context.Add(new PostTag { PostId = post.Id, TagId = tag.Id });
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Bu kodu çalıştırdıktan sonra değişiklik izleyicisi hata ayıklama görünümüne baktığımızda gönderi ve etiketin yeni PostTag
birleştirme varlığıyla ilişkili olduğu gösterilir:
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
PostTags: [{PostId: 3, TagId: 1}]
PostTag {PostId: 3, TagId: 1} Added
PostId: 3 PK FK
TagId: 1 PK FK
Post: {Id: 3}
Tag: {Id: 1}
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
PostTags: [{PostId: 3, TagId: 1}]
üzerinde başvuru gezintileri Post
olduğu gibi üzerinde ve Tag
koleksiyon gezintilerinin PostTag
düzeltildiğine dikkat edin. Bu ilişkiler, önceki tüm örneklerde olduğu gibi FK değerleri yerine gezintiler tarafından işlenebilir. Örneğin, birleştirme varlığında başvuru gezintileri ayarlanarak yukarıdaki kod ilişkiyi eklemek için değiştirilebilir:
context.Add(new PostTag { Post = post, Tag = tag });
Bu, FK'lerde ve gezintilerde önceki örnektekiyle tam olarak aynı değişikliğe neden olur.
Gezintileri atlama
Birleştirme tablosunu el ile düzenlemek zahmetli olabilir. Çoka çok ilişkiler, birleştirme varlığını "atlayan" özel koleksiyon gezintileri kullanılarak doğrudan işlenebilir. Örneğin, yukarıdaki modele iki atlama gezintisi eklenebilir; Biri Gönderiden Etiketlere, diğeri etiketten gönderilere:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; }
public Blog Blog { get; set; }
public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation
public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}
public class Tag
{
public int Id { get; set; }
public string Text { get; set; }
public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation
public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag
public Post Post { get; set; } // Reference navigation
public Tag Tag { get; set; } // Reference navigation
}
Bu çoka çok ilişkisi, atlama gezintilerinin ve normal gezintilerin aynı çoka çok ilişkisi için kullanıldığından emin olmak için aşağıdaki yapılandırmayı gerektirir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j.HasOne(t => t.Tag).WithMany(p => p.PostTags),
j => j.HasOne(t => t.Post).WithMany(p => p.PostTags));
}
Çoka çok ilişkileri eşleme hakkında daha fazla bilgi için bkz . İlişkiler .
Atlama gezintileri normal koleksiyon gezintileri gibi görünür ve davranır. Ancak, yabancı anahtar değerleriyle çalışma yöntemleri farklıdır. Bir gönderiyi bir etiketle ilişkilendirelim, ancak bu kez bir atlama gezintisi kullanarak:
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
post.Tags.Add(tag);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Bu kodun birleştirme varlığını kullanmadığını fark edin. Bunun yerine, bir gezinti koleksiyonuna bire çok ilişkide olduğu gibi bir varlık ekler. Sonuçta elde edilen hata ayıklama görünümü temelde öncekiyle aynıdır:
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
PostTags: [{PostId: 3, TagId: 1}]
Tags: [{Id: 1}]
PostTag {PostId: 3, TagId: 1} Added
PostId: 3 PK FK
TagId: 1 PK FK
Post: {Id: 3}
Tag: {Id: 1}
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
PostTags: [{PostId: 3, TagId: 1}]
Posts: [{Id: 3}]
Birleştirme varlığının bir örneğinin PostTag
, FK değerleri artık ilişkili olan etiketin ve gönderinin PK değerlerine ayarlanmış şekilde otomatik olarak oluşturulduğuna dikkat edin. Tüm normal başvuru ve koleksiyon gezintileri bu FK değerleriyle eşleşecek şekilde düzeltildi. Ayrıca, bu model atlama gezintileri içerdiğinden bunlar da düzeltilmiştir. Özellikle, atla gezintisine Post.Tags
etiketi eklemiş olsak da, Tag.Posts
bu ilişkinin diğer tarafındaki ters atlama gezintisi de ilişkili gönderiyi içerecek şekilde düzeltilmiştir.
Temel alınan çoka çok ilişkilerin, atlama gezintileri en üste katmanlandığında bile doğrudan yönlendirilebileceğini belirtmek gerekir. Örneğin, atla gezintilerini kullanıma sunmadan önce yaptığımız gibi etiket ve Gönderi ilişkilendirilebilir:
context.Add(new PostTag { Post = post, Tag = tag });
Veya FK değerlerini kullanarak:
context.Add(new PostTag { PostId = post.Id, TagId = tag.Id });
Bu, atlama gezintilerinin doğru şekilde düzeltilmesine ve önceki örnektekiyle aynı hata ayıklama görünümü çıkışına neden olmaya devam eder.
Yalnızca gezintileri atla
Önceki bölümde, temel alınan iki bire çok ilişkiyi tam olarak tanımlamaya ek olarak atlama gezintileri ekledik. Bu, FK değerlerine ne olduğunu göstermek için yararlıdır, ancak genellikle gereksizdir. Bunun yerine, çoka çok ilişkisi yalnızca atlama gezintileri kullanılarak tanımlanabilir. Bu belgenin en üstündeki modelde çoka çok ilişkisi bu şekilde tanımlanır. Bu modeli kullanarak, atla gezintisine gönderi ekleyerek (veya alternatif olarak atlama gezintisine Tag.Posts
etiket ekleyerek) gönderiyi Post.Tags
ve Etiketi yeniden ilişkilendirebiliriz:
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
post.Tags.Add(tag);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Bu değişikliği yaptıktan sonra hata ayıklama görünümüne baktığımızda EF Core'un birleştirme varlığını temsil eden bir örneği Dictionary<string, object>
oluşturduğunu gösterir. Bu birleştirme varlığı, ilişkili gönderi ve TagsId
etiketin PK değerleriyle eşleşecek şekilde ayarlanmış hem hem de PostsId
yabancı anahtar özelliklerini içerir.
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: [{Id: 1}]
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
Posts: [{Id: 3}]
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 1} Added
PostsId: 3 PK FK
TagsId: 1 PK FK
Örtük birleştirme varlıkları ve varlık türlerinin Dictionary<string, object>
kullanımı hakkında daha fazla bilgi için bkz. İlişkiler.
Önemli
Birleştirme varlık türleri için kurala göre kullanılan CLR türü, performansı geliştirmek için gelecek sürümlerde değişebilir. Bu açıkça yapılandırılmadığı sürece birleştirme türüne Dictionary<string, object>
bağımlı olmayın.
Yükleri olan varlıkları birleştirme
Şimdiye kadar tüm örnekler, çoka çok ilişkisi için gereken iki yabancı anahtar özelliğini içeren bir birleştirme varlık türü (açık veya örtük) kullandı. Bu FK değerlerinin, ilişkileri işlerken uygulama tarafından açıkça ayarlanması gerekmez çünkü değerleri ilgili varlıkların birincil anahtar özelliklerinden gelir. Bu, EF Core'un eksik veri olmadan birleştirme varlığının örneklerini oluşturmasına olanak tanır.
Oluşturulan değerlere sahip yükler
EF Core, birleştirme varlık türüne ek özellikler eklemeyi destekler. Bu, birleştirme varlığına bir "yük" vermek olarak bilinir. Örneğin, birleştirme varlığına özellik PostTag
ekleyelimTaggedOn
:
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag
public DateTime TaggedOn { get; set; } // Payload
}
EF Core bir birleştirme varlık örneği oluşturduğunda bu payload özelliği ayarlanmaz. Bununla başa çıkmanın en yaygın yolu, otomatik olarak oluşturulan değerlerle yük özelliklerini kullanmaktır. Örneğin özelliği, TaggedOn
her yeni varlık eklendiğinde depo tarafından oluşturulan bir zaman damgası kullanacak şekilde yapılandırılabilir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j.HasOne<Tag>().WithMany(),
j => j.HasOne<Post>().WithMany(),
j => j.Property(e => e.TaggedOn).HasDefaultValueSql("CURRENT_TIMESTAMP"));
}
Bir gönderi artık öncekiyle aynı şekilde etiketlenebilir:
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
post.Tags.Add(tag);
context.SaveChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
SaveChanges çağrıldıktan sonra değişiklik izleyicisi hata ayıklama görünümüne baktığımızda payload özelliğinin uygun şekilde ayarlandığı gösterilir:
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: [{Id: 1}]
PostTag {PostId: 3, TagId: 1} Unchanged
PostId: 3 PK FK
TagId: 1 PK FK
TaggedOn: '12/29/2020 8:13:21 PM'
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
Posts: [{Id: 3}]
Yük değerlerini açıkça ayarlama
Önceki örnekten itibaren, otomatik olarak oluşturulan değeri kullanmayan bir payload özelliği ekleyelim:
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag
public DateTime TaggedOn { get; set; } // Auto-generated payload property
public string TaggedBy { get; set; } // Not-generated payload property
}
Bir gönderi artık önceki gibi etiketlenebilir ve birleştirme varlığı otomatik olarak oluşturulmaya devam eder. Daha sonra bu varlığa İzlenen Varlıklara Erişme bölümünde açıklanan mekanizmalardan biri kullanılarak erişilebilir. Örneğin, aşağıdaki kod birleştirme varlık örneğine erişmek için kullanır DbSet<TEntity>.Find :
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
post.Tags.Add(tag);
context.ChangeTracker.DetectChanges();
var joinEntity = context.Set<PostTag>().Find(post.Id, tag.Id);
joinEntity.TaggedBy = "ajcvickers";
context.SaveChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Birleştirme varlığı bulunduktan sonra, SaveChanges çağrılmadan önce payload özelliğini ayarlamak TaggedBy
için normal şekilde işlenebilir.
Dekont
EF Core'a ChangeTracker.DetectChanges() gezinti özelliği değişikliğini algılama ve kullanılmadan önce Find
birleştirme varlık örneğini oluşturma şansı vermek için burada çağrısı yapılması gerektiğini unutmayın. Daha fazla bilgi için bkz . Değişiklik Algılama ve Bildirimler .
Alternatif olarak, bir gönderiyi bir etiketle ilişkilendirmek için birleştirme varlığı açıkça oluşturulabilir. Örnek:
using var context = new BlogsContext();
var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);
context.Add(
new PostTag { PostId = post.Id, TagId = tag.Id, TaggedBy = "ajcvickers" });
context.SaveChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Son olarak, yük verilerini ayarlamanın bir diğer yolu da veritabanını güncelleştirmeden önce varlıkları işlemek için olayı geçersiz kılması SaveChanges veya kullanılmasıdır DbContext.SavingChanges . Örnek:
public override int SaveChanges()
{
foreach (var entityEntry in ChangeTracker.Entries<PostTag>())
{
if (entityEntry.State == EntityState.Added)
{
entityEntry.Entity.TaggedBy = "ajcvickers";
}
}
return base.SaveChanges();
}