Aracılığıyla paylaş


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:

  1. Veritabanına eklenecek yeni varlıklar oluşturma.
  2. 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 Addedizliyor:

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 Addedizliyor:

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 Unchangedizliyor:

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 Unchangediş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 Unchangedneden 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 Modifiedizlemeye 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 Attachile Add olduğu gibi, Update ilgili varlıkların grafiğinin tamamını olarak Modifiedişaretler. Örneğin, mevcut bir blogu ve ilişkili mevcut gönderileri olarak Modifiedeklemek 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 Modifiedizliyor:

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 Attacholduğu gibi, oluşturulan anahtar değerleri için Updatede 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 Modifiediş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 Deletediş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 newoluşturulan bir varlıkta çağrı Remove yapmak olağan dışıdır. Ayrıca , ve Update'den farklı Attach Addolarak, 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:

  1. Varlıklar için sorgu çalıştırma
  2. Attach Önceki bölümlerde açıklandığı gibi bağlantısı kesilmiş varlıkların grafiğinde veya Update 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 Deletedtam 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 Unchangediş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:

  1. 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.
  2. 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 Deletediş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 Modifiediş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 Deletediş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ş Deletedolması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 AddAttach ç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 void 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}");
        });

    context.SaveChanges();
}

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 Addedişaretlenir. Anahtar ayarlanırsa ve değer negatif değilse, varlık olarak Modifiedişaretlenir. Ancak, negatif bir anahtar değeri bulunursa gerçek, negatif olmayan değeri geri yüklenir ve varlık olarak Deletedizlenir.

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ı Idbir 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.