Varlıkları Açıkça İzleme
Her DbContext örneği, varlıklarda yapılan değişiklikleri izler. Bu izlenen varlıklar da SaveChanges çağrıldığında değişiklikleri veritabanına yönlendirir.
Entity Framework Core (EF Core) değişiklik izlemesi en iyi sonucu, varlıkları sorgulamak ve çağırarak SaveChangesgüncelleştirmek için aynı DbContext örnek kullanıldığında çalışır. Bunun nedeni EF Core’un sorgulanan varlıkların durumunu otomatik olarak izlemesi ve SaveChanges çağrıldığında bu varlıklarda yapılan değişiklikleri algılamasıdır. Bu yaklaşım EF Core'daki Değişiklik İzleme ele alınmıştır.
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.
Bahşiş
Kolaylık olması için, bu belgede SaveChangesAsync gibi zaman uyumsuz eşdeğerleri yerine SaveChanges gibi zaman uyumlu yöntemler kullanılır ve bunlara başvuru yapılır. Aksi belirtilmediği sürece çağrı ve bekleme örneklerinde zaman uyumsuz yöntemler de kullanılabilir.
Giriş
Varlıklar, bağlamın bu varlıkları izlemesi için açıkça " DbContext eklenebilir". Bu, aşağıdaki durumlarda öncelikli olarak yararlıdır:
- Veritabanına eklenecek yeni varlıklar oluşturma.
- Daha önce farklı bir DbContext örneği tarafından sorgulanmış bağlantısı kesilmiş varlıkları yeniden ekleme.
Bunlardan ilki çoğu uygulama için gerekli olacaktır ve öncelikle yöntemler tarafından DbContext.Add işlenir.
İkincisi yalnızca varlıklar izlenmediği sırada varlıkları veya ilişkilerini değiştiren uygulamalar tarafından gereklidir. Örneğin, bir web uygulaması, kullanıcının değişiklik yaptığı ve varlıkları geri gönderdiği web istemcisine varlık gönderebilir. Başlangıçta DbContext'ten sorgulandıkları, ancak istemciye gönderildiğinde bu bağlamla bağlantısı kesildiği için bu varlıklar "bağlantısı kesildi" olarak adlandırılır.
Web uygulamasının artık bu varlıkları yeniden eklemesi ve bu varlıkların yeniden izlenmesi ve veritabanında uygun güncelleştirmeler yapabilecek şekilde SaveChanges yapılan değişiklikleri belirtmesi gerekir. Bu öncelikle ve DbContext.Update yöntemleri tarafından DbContext.Attach işlenir.
Bahşiş
Varlıkların sorgulandığı aynı DbContext örneğine eklenmesi normalde gerekli olmamalıdır. Düzenli olarak bir izleme yok sorgusu gerçekleştirip döndürülen varlıkları aynı bağlama eklemeyin. Bu, bir izleme sorgusu kullanmaktan daha yavaş olur ve gölge özellik değerlerinin eksik olması gibi sorunlarla da sonuçlanabilir ve bu da düzeltmeyi zorlaştırır.
Oluşturulan ve açık anahtar değerleri
Varsayılan olarak, tamsayı ve GUID anahtar özellikleri otomatik olarak oluşturulan anahtar değerlerini kullanacak şekilde yapılandırılır. Bunun değişiklik izleme için önemli bir avantajı vardır: ayarlanmamış anahtar değeri varlığın "yeni" olduğunu gösterir. "new" ile, henüz veritabanına eklenmediği anlamına gelir.
Aşağıdaki bölümlerde iki model kullanılmıştır. birincisi, oluşturulan anahtar değerlerini kullanmayacak şekilde yapılandırılır:
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
public IList<Post> Posts { get; } = new List<Post>();
}
public class Post
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
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; }
}
Oluşturulmamış (açıkça ayarlanmış) anahtar değerleri her örnekte ilk olarak gösterilir çünkü her şey çok açık ve takip etmek kolaydır. Bunun ardından, oluşturulan anahtar değerlerinin kullanıldığı bir örnek gelir:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Post> Posts { get; } = new List<Post>();
}
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; }
}
Oluşturulan anahtar değerlerinin kullanılması basit tamsayı anahtarları için varsayılan değer olduğundan, bu modeldeki anahtar özelliklerinin burada ek yapılandırmaya gerek olmadığına dikkat edin.
Yeni varlıklar ekleme
Açık anahtar değerleri
Tarafından eklenecek SaveChangesdurumda bir varlığın Added
izlenmesi gerekir. Varlıklar genellikle üzerinde DbSet<TEntity>, , DbContext.AddRange, DbContext.AddAsyncveya DbContext.AddRangeAsynceşdeğer yöntemlerinden biri DbContext.Addçağrılarak Eklendi durumuna konur.
Bahşiş
Bu yöntemlerin tümü, değişiklik izleme bağlamında aynı şekilde çalışır. Daha fazla bilgi için bkz. Ek Değişiklik İzleme Özellikleri.
Örneğin, yeni bir blogu izlemeye başlamak için:
context.Add(
new Blog { Id = 1, Name = ".NET Blog", });
Bu çağrının ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek, bağlamın şu durumdaki yeni varlığı izlediğini Added
gösterir:
Blog {Id: 1} Added
Id: 1 PK
Name: '.NET Blog'
Posts: []
Ancak, Add yöntemleri yalnızca tek bir varlık üzerinde çalışmaz. Aslında ilgili varlıkların tüm grafını izlemeye başlar ve hepsini Added
duruma geçirir. Örneğin, yeni bir blog ve ilişkili yeni gönderiler eklemek için:
context.Add(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Bağlam artık tüm bu varlıkları olarak Added
izliyor:
Blog {Id: 1} Added
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Added
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}
Post {Id: 2} Added
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}
Yukarıdaki örneklerde anahtar özellikleri için açık değerlerin Id
ayarlandığına dikkat edin. Bunun nedeni, buradaki modelin otomatik olarak oluşturulan anahtar değerleri yerine açıkça ayarlanan anahtar değerlerini kullanacak şekilde yapılandırılmış olmasıdır. Oluşturulan anahtarları kullanmadığınızda, anahtar özellikleri çağrılmadan Add
önce açıkça ayarlanmalıdır. Daha sonra SaveChanges çağrıldığında bu anahtar değerleri eklenir. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Id", "Name")
VALUES (@p0, @p1);
-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String), @p3='1' (DbType = String), @p4='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p5='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p2, @p3, @p4, @p5);
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String), @p1='1' (DbType = String), @p2='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p3='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2, @p3);
Bu varlıklar artık veritabanında mevcut olduğundan, bu varlıkların Unchanged
tümü SaveChanges tamamlandıktan sonra durumunda izlenir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {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}
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}
Oluşturulan anahtar değerleri
Yukarıda belirtildiği gibi, tamsayı ve GUID anahtar özellikleri varsayılan olarak otomatik olarak oluşturulan anahtar değerlerini kullanacak şekilde yapılandırılır. Bu, uygulamanın açıkça herhangi bir anahtar değeri ayarlamaması gerektiği anlamına gelir. Örneğin, yeni bir blog ve oluşturulan anahtar değerleri içeren gönderiler eklemek için:
context.Add(
new Blog
{
Name = ".NET Blog",
Posts =
{
new Post
{
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Açık anahtar değerlerinde olduğu gibi bağlam artık tüm bu varlıkları olarak Added
izliyor:
Blog {Id: -2147482644} Added
Id: -2147482644 PK Temporary
Name: '.NET Blog'
Posts: [{Id: -2147482637}, {Id: -2147482636}]
Post {Id: -2147482637} Added
Id: -2147482637 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: -2147482644}
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: -2147482644}
Bu durumda , her varlık için geçici anahtar değerlerinin oluşturulduğuna dikkat edin. Bu değerler, SaveChanges çağrılana kadar EF Core tarafından kullanılır ve bu noktada gerçek anahtar değerleri veritabanından geri okunur. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Name")
VALUES (@p0);
SELECT "Id"
FROM "Blogs"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p2='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p3='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p1, @p2, @p3);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
SaveChanges tamamlandıktan sonra, tüm varlıklar gerçek anahtar değerleriyle güncelleştirildi ve artık veritabanındaki Unchanged
durumla eşleştiklerinden bu durumda izlenir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {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}
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}
Bu, açık anahtar değerlerini kullanan önceki örnekle tamamen aynı son durumdur.
Bahşiş
Oluşturulan anahtar değerleri kullanılırken bile açık bir anahtar değeri ayarlanabilir. EF Core daha sonra bu anahtar değerini kullanarak eklemeye çalışır. Kimlik sütunları içeren SQL Server da dahil olmak üzere bazı veritabanı yapılandırmaları bu tür eklemeleri desteklemez ve oluşturur (geçici bir çözüm için bu belgelere bakın).
Mevcut varlıkları ekleme
Açık anahtar değerleri
Sorgulardan döndürülen varlıklar durumunda Unchanged
izlenir. Durum, Unchanged
varlığın sorgulandıktan sonra değiştirilmediği anlamına gelir. Http isteğindeki bir web istemcisinden döndürülen bağlantısız varlık, üzerinde , DbContext.AttachRangeveya eşdeğer yöntemler DbSet<TEntity>kullanılarak DbContext.Attachbu duruma getirilebilir. Örneğin, var olan bir blogu izlemeye başlamak için:
context.Attach(
new Blog { Id = 1, Name = ".NET Blog", });
Dekont
Buradaki örnekler, kolaylık sağlamak için açıkça ile new
varlık oluşturmaktır. Normalde varlık örnekleri, istemciden seri durumdan çıkarılma veya HTTP Post'taki verilerden oluşturulma gibi başka bir kaynaktan gelir.
Bu çağrının ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek varlığın şu durumda Unchanged
izlendiğini gösterir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: []
gibi Add
, Attach
aslında bağlı varlıkların tüm grafiğini Unchanged
duruma ayarlar. Örneğin, var olan bir blogu ve ilişkili mevcut gönderileri eklemek için:
context.Attach(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Bağlam artık tüm bu varlıkları olarak Unchanged
izliyor:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {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}
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}
Bu noktada SaveChanges çağrısının hiçbir etkisi olmayacaktır. Tüm varlıklar olarak Unchanged
işaretlendiğinden, veritabanında güncelleştirilecek bir şey yoktur.
Oluşturulan anahtar değerleri
Yukarıda belirtildiği gibi, tamsayı ve GUID anahtar özellikleri varsayılan olarak otomatik olarak oluşturulan anahtar değerlerini kullanacak şekilde yapılandırılır. Bu, bağlantısız varlıklarla çalışırken büyük bir avantaja sahiptir: ayarlanmamış anahtar değeri varlığın henüz veritabanına eklenmediğini gösterir. Bu, değişiklik izleyicisinin yeni varlıkları otomatik olarak algılamasını ve duruma yerleştirmesini Added
sağlar. Örneğin, blogun ve gönderilerin bu grafiğini eklemeyi göz önünde bulundurun:
context.Attach(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});
Blog, veritabanında zaten var olduğunu belirten 1 anahtar değerine sahiptir. Gönderilerin ikisinde de anahtar değerleri ayarlanmıştır, ancak üçüncüsü ayarlanmaz. EF Core bu anahtar değerini bir tamsayı için varsayılan CLR olan 0 olarak görür. Bu, EF Core'un yeni varlığı yerine olarak Added
işaretlemesine Unchanged
neden olur:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482636}]
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {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}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Bu noktada SaveChanges çağrısı varlıklarla Unchanged
hiçbir şey yapmaz, ancak yeni varlığı veritabanına ekler. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Burada dikkat edilmesi gereken önemli nokta, oluşturulan anahtar değerlerle EF Core'un bağlantısı kesilmiş bir grafikteki mevcut varlıklardan yenileri otomatik olarak ayırt edebilmesidir. Özetle, oluşturulan anahtarları kullanırken EF Core her zaman bu varlığın anahtar değeri ayarlı olmadığında bir varlık ekler.
Mevcut varlıkları güncelleştirme
Açık anahtar değerleri
DbContext.Update, DbContext.UpdateRangeve üzerindeki DbSet<TEntity> eşdeğer yöntemler, varlıkların Attach
durum yerine Unchanged
içine Modified
koyulmalarının dışında, yukarıda açıklanan yöntemlerle tam olarak aynı şekilde davranır. Örneğin, mevcut blogu olarak Modified
izlemeye başlamak için:
context.Update(
new Blog { Id = 1, Name = ".NET Blog", });
Bu çağrının ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek, bağlamın bu varlığı şu durumda izlediğini Modified
gösterir:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: []
ve Attach
ile Add
olduğu gibi, Update
ilgili varlıkların grafiğinin tamamını olarak Modified
işaretler. Örneğin, mevcut bir blogu ve ilişkili mevcut gönderileri olarak Modified
eklemek için:
context.Update(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Bağlam artık tüm bu varlıkları olarak Modified
izliyor:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}
Bu noktada SaveChanges çağrısı, tüm bu varlıklar için veritabanına güncelleştirmelerin gönderilmesine neden olur. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
Oluşturulan anahtar değerleri
'de Attach
olduğu gibi, oluşturulan anahtar değerleri için Update
de aynı büyük avantaja sahiptir: ayarlanmamış anahtar değeri varlığın yeni olduğunu ve henüz veritabanına eklenmediğini gösterir. gibi Attach
, bu da DbContext'in yeni varlıkları otomatik olarak algılamasını ve bu varlıkları duruma yerleştirmesini Added
sağlar. Örneğin, bir blog ve gönderinin bu grafiğiyle arama Update
yapmayı göz önünde bulundurun:
context.Update(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});
Attach
Örnekte olduğu gibi anahtar değeri olmayan gönderi yeni olarak algılanır ve duruma ayarlanırAdded
. Diğer varlıklar olarak Modified
işaretlenir:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482633}]
Post {Id: -2147482633} Added
Id: -2147482633 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {Id: 1}
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}
Bu noktada çağrılması SaveChanges
, yeni varlık eklenirken mevcut tüm varlıklar için veritabanına güncelleştirmelerin gönderilmesine neden olur. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Bu, bağlantısız bir grafikten güncelleştirmeler ve eklemeler oluşturmanın çok kolay bir yoludur. Ancak, bazı özellik değerleri değiştirilmemiş olsa bile izlenen her varlığın her özelliği için veritabanına güncelleştirmelerin veya eklemelerin gönderilmesiyle sonuçlanır. Bundan çok korkma. küçük grafiklere sahip birçok uygulama için bu, güncelleştirme oluşturmanın kolay ve pragmatik bir yolu olabilir. Diğer daha karmaşık desenler, EF Core'da Kimlik Çözümlemesi'nde açıklandığı gibi bazen daha verimli güncelleştirmelere neden olabilir.
Mevcut varlıkları silme
Bir varlığın SaveChanges tarafından silinmesi için durumunda izlenmelidir Deleted
. Varlıklar genellikle üzerinde , DbContext.RemoveRangeveya eşdeğer yöntemlerinden DbSet<TEntity>biri DbContext.Removeçağrılarak duruma geçirilirDeleted
. Örneğin, var olan bir gönderiyi olarak Deleted
işaretlemek için:
context.Remove(
new Post { Id = 2 });
Bu çağrının ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek, bağlamın durumdaki varlığı izlediğini Deleted
gösterir:
Post {Id: 2} Deleted
Id: 2 PK
BlogId: <null> FK
Content: <null>
Title: <null>
Blog: <null>
SaveChanges çağrıldığında bu varlık silinir. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
SaveChanges tamamlandıktan sonra, silinen varlık veritabanında artık mevcut olmadığından DbContext'ten ayrılır. Bu nedenle hiçbir varlık izlenmediğinden hata ayıklama görünümü boş olur.
Bağımlı/alt varlıkları silme
Bir grafikten bağımlı/alt varlıkları silmek, asıl/üst varlıkları silmekten daha kolaydır. Daha fazla bilgi için bir sonraki bölüme ve Yabancı Anahtarları ve Gezintileri Değiştirme bölümüne bakın.
ile new
oluşturulan bir varlıkta çağrı Remove
yapmak olağan dışıdır. Ayrıca , ve Update
'den farklı Attach
Add
olarak, veya Modified
durumunda henüz izlenmeyen Unchanged
bir varlığı çağırmak Remove
sık karşılaşılan bir durumdur. Bunun yerine, ilgili varlıkların tek bir varlığını veya grafiğini izlemek ve ardından silinmesi gereken varlıkları çağırmak Remove
normaldir. İzlenen varlıkların bu grafiği genellikle aşağıdakilerden biri tarafından oluşturulur:
- Varlıklar için sorgu çalıştırma
Attach
Önceki bölümlerde açıklandığı gibi bağlantısı kesilmiş varlıkların grafiğinde veyaUpdate
yöntemlerini kullanma.
Örneğin, önceki bölümdeki kodun bir istemciden gönderi alma ve ardından aşağıdaki gibi bir işlem yapma olasılığı daha yüksektir:
context.Attach(post);
context.Remove(post);
İzlenmeyen bir varlıkta çağrılması Remove
önce eklenmesine ve ardından olarak işaretlenmesine neden olduğundan, bu durum önceki örnekle Deleted
tam olarak aynı şekilde davranır.
Daha gerçekçi örneklerde, önce varlıkların grafiği eklenir ve ardından bu varlıkların bazıları silinmiş olarak işaretlenir. Örnek:
// Attach a blog and associated posts
context.Attach(blog);
// Mark one post as Deleted
context.Remove(blog.Posts[1]);
Üzerinde çağrılan Remove
varlık dışında tüm varlıklar olarak Unchanged
işaretlenir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {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}
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: {Id: 1}
SaveChanges çağrıldığında bu varlık silinir. Örneğin, SQLite kullanılırken:
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
SaveChanges tamamlandıktan sonra, silinen varlık veritabanında artık mevcut olmadığından DbContext'ten ayrılır. Diğer varlıklar şu Unchanged
durumda kalır:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
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}
Asıl/üst varlıkları silme
İki varlık türünü bağlayan her ilişkinin bir sorumlusu veya üst ucu ve bağımlı veya alt ucu vardır. Bağımlı/alt varlık, yabancı anahtar özelliğine sahip olan varlıktır. Bire çok ilişkisinde, asıl/üst öğe "bir" tarafında ve bağımlı/alt öğe "çok" tarafındadır. Daha fazla bilgi için bkz . İlişkiler .
Yukarıdaki örneklerde, blog gönderilerinde bire çok ilişkisinde bağımlı/alt varlık olan bir gönderiyi siliyorduk. Bağımlı/alt varlığın kaldırılması diğer varlıklar üzerinde herhangi bir etkiye sahip olmadığından bu oldukça basittir. Öte yandan, bir asıl/üst varlığın silinmesi, bağımlı/alt varlıkları da etkilemelidir. Bunun yapılmaması, artık var olmayan bir birincil anahtar değerine başvuran yabancı anahtar değeri bırakır. Bu geçersiz bir model durumudur ve çoğu veritabanında başvuru kısıtlaması hatasıyla sonuçlanır.
Bu geçersiz model durumu iki şekilde işlenebilir:
- FK değerlerini null olarak ayarlama. Bu, bağımlıların/alt öğelerinin artık hiçbir sorumlu/üst öğeyle ilişkili olmadığını gösterir. Bu, yabancı anahtarın null atanabilir olması gereken isteğe bağlı ilişkiler için varsayılan değerdir. FK'nin null olarak ayarlanması, yabancı anahtarın genellikle null atanamaz olduğu gerekli ilişkiler için geçerli değildir.
- Bağımlıları/alt öğeleri silme. Bu, gerekli ilişkiler için varsayılandır ve isteğe bağlı ilişkiler için de geçerlidir.
Değişiklik izleme ve ilişkiler hakkında ayrıntılı bilgi için bkz . Yabancı Anahtarları ve Gezintileri Değiştirme.
İsteğe bağlı ilişkiler
Yabancı Post.BlogId
anahtar özelliği, kullanmakta olduğumuz modelde null atanabilir. Bu, ilişkinin isteğe bağlı olduğu ve dolayısıyla EF Core'un varsayılan davranışının blog silindiğinde yabancı anahtar özelliklerini null olarak ayarlamak BlogId
olduğu anlamına gelir. Örnek:
// Attach a blog and associated posts
context.Attach(blog);
// Mark the blog as deleted
context.Remove(blog);
Çağrısının Remove
ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek, beklendiği gibi blog'un şu şekilde Deleted
işaretlendiğini gösterir:
Blog {Id: 1} Deleted
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: <null> FK Modified Originally 1
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
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>
Daha da ilginç olan, tüm ilgili gönderilerin olarak Modified
işaretlenmesidir. Bunun nedeni, her varlıktaki yabancı anahtar özelliğinin null olarak ayarlanmış olmasıdır. SaveChanges çağrısı, blogu silmeden önce her gönderi için yabancı anahtar değerini veritabanında null olarak güncelleştirir:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p2;
SELECT changes();
SaveChanges tamamlandıktan sonra, silinen varlık veritabanında artık mevcut olmadığından DbContext'ten ayrılır. Diğer varlıklar artık veritabanının durumuyla eşleşen null yabancı anahtar değerleriyle işaretlenir Unchanged
:
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: <null> FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: <null> FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Gerekli ilişkiler
Post.BlogId
Yabancı anahtar özelliği null değer atanamazsa bloglar ve gönderiler arasındaki ilişki "gerekli" olur. Bu durumda EF Core, asıl/üst öğe silindiğinde varsayılan olarak bağımlı/alt varlıkları siler. Örneğin, önceki örnekte olduğu gibi ilgili gönderileri içeren bir blog siliniyor:
// Attach a blog and associated posts
context.Attach(blog);
// Mark the blog as deleted
context.Remove(blog);
Çağrısının Remove
ardından değişiklik izleyicisi hata ayıklama görünümünü incelemek, beklendiği gibi blog'un yeniden olarak Deleted
işaretlendiğini gösterir:
Blog {Id: 1} Deleted
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Deleted
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}
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: {Id: 1}
Bu durumda daha ilginç olan, tüm ilgili gönderilerin olarak işaretlenmiş Deleted
olmasıdır. SaveChanges çağrısı blog ve tüm ilgili gönderilerin veritabanından silinmesine neden olur:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p1;
SaveChanges tamamlandıktan sonra, silinen tüm varlıklar artık veritabanında bulunmadığından DbContext'ten ayrılır. Bu nedenle hata ayıklama görünümünden alınan çıkış boş olur.
Dekont
Bu belge yalnızca EF Core'daki ilişkilerle çalışma yüzeyini çizer. SaveChanges'i çağırırken bağımlı/alt varlıkları güncelleştirme/silme hakkında daha fazla bilgi için bkz. İlişkileri modelleme ve Yabancı Anahtarları ve Gezintileri Değiştirme.
TrackGraph ile özel izleme
ChangeTracker.TrackGraphgibi Add
Attach
çalışır ve Update
izlemeden önce her varlık örneği için bir geri çağırma oluşturması dışında. Bu, graftaki tek tek varlıkların nasıl izlenecekleri belirlenirken özel mantığın kullanılmasına olanak tanır.
Örneğin, EF Core'un oluşturulan anahtar değerlerine sahip varlıkları takip ederken kullandığı kuralı göz önünde bulundurun: anahtar değeri sıfırsa, varlık yenidir ve eklenmelidir. Bu kuralı genişleterek anahtar değerinin negatif olup olmadığını, varlığın silinmesi gerektiğini belirtelim. Bu, bağlantısı kesilmiş bir grafiğin varlıklarındaki birincil anahtar değerlerini değiştirerek silinen varlıkları işaretlememizi sağlar:
blog.Posts.Add(
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
}
);
var toDelete = blog.Posts.Single(e => e.Title == "Announcing F# 5");
toDelete.Id = -toDelete.Id;
Bu bağlantısız grafik trackGraph kullanılarak izlenebilir:
public static async Task UpdateBlog(Blog blog)
{
using var context = new BlogsContext();
context.ChangeTracker.TrackGraph(
blog, node =>
{
var propertyEntry = node.Entry.Property("Id");
var keyValue = (int)propertyEntry.CurrentValue;
if (keyValue == 0)
{
node.Entry.State = EntityState.Added;
}
else if (keyValue < 0)
{
propertyEntry.CurrentValue = -keyValue;
node.Entry.State = EntityState.Deleted;
}
else
{
node.Entry.State = EntityState.Modified;
}
Console.WriteLine($"Tracking {node.Entry.Metadata.DisplayName()} with key value {keyValue} as {node.Entry.State}");
});
await context.SaveChangesAsync();
}
Grafikteki her varlık için yukarıdaki kod, varlığı izlemeden önce birincil anahtar değerini denetler. Ayarsız (sıfır) anahtar değerleri için kod, EF Core'un normalde yapacağı şeyi yapar. Diğer bir ifadeyle, anahtar ayarlanmadıysa varlık olarak Added
işaretlenir. Anahtar ayarlanırsa ve değer negatif değilse, varlık olarak Modified
işaretlenir. Ancak, negatif bir anahtar değeri bulunursa gerçek, negatif olmayan değeri geri yüklenir ve varlık olarak Deleted
izlenir.
Bu kodu çalıştırmanın çıkışı şu şekildedir:
Tracking Blog with key value 1 as Modified
Tracking Post with key value 1 as Modified
Tracking Post with key value -2 as Deleted
Tracking Post with key value 0 as Added
Dekont
Kolaylık olması için, bu kod her varlığın adlı Id
bir tamsayı birincil anahtar özelliği olduğunu varsayar. Bu, soyut bir temel sınıf veya arabirimde codified olabilir. Alternatif olarak, bu kodun herhangi bir varlık türüyle çalışması için birincil anahtar özelliği veya özellikleri meta verilerden IEntityType alınabilir.
TrackGraph'ın iki aşırı yüklemesi vardır. Yukarıda kullanılan basit aşırı yüklemede EF Core, graftan geçişin ne zaman durdurulacağını belirler. Özellikle, söz konusu varlık zaten izlendiğinde veya geri arama varlığı izlemeye başlamadığında belirli bir varlıktan yeni ilgili varlıkları ziyaret etmez.
Gelişmiş aşırı yükleme, ChangeTracker.TrackGraph<TState>(Object, TState, Func<EntityEntryGraphNode<TState>,Boolean>)bool döndüren bir geri çağırmaya sahiptir. Geri çağırma false döndürürse, grafik geçişi durur, aksi takdirde devam eder. Bu aşırı yükleme kullanılırken sonsuz döngülerden kaçınmak için dikkatli olunmalıdır.
Gelişmiş aşırı yükleme, TrackGraph'a durum sağlanmasına da olanak tanır ve bu durum daha sonra her geri çağırmaya geçirilir.