Aracılığıyla paylaş


Varlıkları Açıkça İzleme

Her DbContext örnek, varlıklarda yapılan değişiklikleri izler. İzlenen bu varlıklar, SaveChanges çağrıldığında veritabanındaki değişikliklere yol açar.

Entity Framework Core (EF Core) değişiklik izlemesi, varlıkları sorgulamak ve DbContext çağrılarak güncelleştirmek için aynı SaveChanges örnek kullanıldığında en iyi şekilde ç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'da Değişiklik İzleme bölümünde ele alınmıştır.

Tavsiye

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 bkz. EF Core'da Değişiklik İzleme .

Tavsiye

GitHub'dan örnek kodu indirerek bu belgedeki tüm kodu çalıştırabilir ve hatalarını ayıklayabilirsiniz.

Giriş

Varlıklar, bağlamın bu varlıkları izlemesini sağlamak amacıyla açık bir şekilde DbContext'ye "bağlanabilir". 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ış olan bağlantısı kesilmiş varlıkları yeniden bağlama.

Çoğu uygulama için gerekli olacak olan birincisi, öncelikli olarak DbContext.Add yöntemleri tarafından 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, bu varlıkları yeniden ilişkilendirip yeniden izlemeye alması ve SaveChanges'in veritabanında uygun güncellemeleri yapabilmesi için yapılan değişiklikleri belirtmesi gerekir. Bu öncelikle DbContext.Attach ve DbContext.Update yöntemleri tarafından işlenir.

Tavsiye

Varlıkların sorgulandığı aynı DbContext örneğine eklenmesi normalde gerekli olmamalıdır. Rutin olarak izleme yapılmayan bir sorgu 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 özellikleriotomatik 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

Bir varlığın Added durumunda SaveChanges tarafından eklenebilmesi için izlenmesi gerekir. Varlıklar genellikle DbContext.Add, DbContext.AddRange, DbContext.AddAsync, DbContext.AddRangeAsync veya DbSet<TEntity> üzerindeki eşdeğer yöntemlerden biri çağrılarak Eklendi durumuna konur.

Tavsiye

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 grafiğini 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ı Added olarak 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 özellikler için Id belirgin değerlerin 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ğırmadanAdd ö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.

Tavsiye

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 ekleme işlemlerini desteklemez ve hata verir (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 Unchanged durumunda takip edilir. Durum, Unchanged varlığın sorgulandıktan sonra değiştirilmediği anlamına gelir. HTTP isteğiyle bir web istemcisinden dönen bağlantısız bir varlık, DbContext.Attach, DbContext.AttachRange veya DbSet<TEntity> üzerindeki eşdeğer yöntemler kullanılarak bu duruma getirilebilir. Örneğin, var olan bir blogu izlemeye başlamak için:

context.Attach(
    new Blog { Id = 1, Name = ".NET Blog", });

Uyarı

Buradaki örneklerde basitlik açısından new ile açıkça varlık oluşturulmaktadı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ı Unchanged olarak 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 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 onları Added durumuna yerleştirmesini 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 anahtar değerleri ayarlanmıştır, ancak üçüncüsünde ayarlanmamıştır. 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ığı Added yerine Unchanged olarak işaretlemesine yol açar.

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ı, Unchanged varlıklar üzerinde hiçbir etki yaratmaz, 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.UpdateRange ve DbSet<TEntity> üzerindeki eşdeğer yöntemler, varlıkların Attach yerine Modified durumuna Unchanged konulmalarının dışında, yukarıda açıklanan yöntemlerle tam olarak aynı şekilde davranır. Örneğin, mevcut bir blogu 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 Addile Attach 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 Modified olarak iliştirmek 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ı Modified olarak 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 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. Tıpkı Attach gibi, bu da DbContext'in yeni varlıkları otomatik olarak algılamasını ve onları Added duruma koymasını sağlar. Örneğin, bir blog ve gönderilerin bu grafiğiyle Update'yi çağırmayı düşünün.

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}

SaveChanges kullanıldığında, yeni varlık eklenirken mevcut tüm varlıklar için güncelleştirmeler veritabanına gönderilecektir. Ö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 ile silinebilmesi için Deleted durumunda takip edilmesi gerekir. Varlıklar genellikle Deleted, DbContext.Remove veya eşdeğer yöntemlerden biri DbContext.RemoveRange üzerinde çağrılarak DbSet<TEntity> durumuna geçirilir. Ö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.

Remove ile oluşturulan bir varlıkta new çağrısı yapmak olağandışıdır. Ayrıca, Add, Attach ve Update'den farklı olarak, Remove çağırmak Unchanged veya Modified durumunda zaten izlenmeyen bir varlık için nadiren karşılaşılan bir durumdur. Bunun yerine, genellikle tek bir varlığı veya ilişkili varlıklar grafiğini izlemek ve ardından silinmesi gereken varlıklar için Remove işlevini çağırmak yaygındır. İ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. Önceki bölümlerde açıklandığı gibi, bağlantısı kesilmiş varlıkların grafiği üzerinde Attach 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. Örneğin:

// Attach a blog and associated posts
context.Attach(blog);

// Mark one post as Deleted
context.Remove(blog.Posts[1]);

Üzerinde çağrılan Unchanged varlık dışında tüm varlıklar olarak Removeiş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 Unchanged durumunda 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 ana veya ebeveyn ucu ve bir bağımlı veya alt ucu vardır. Bağımlı/alt varlık, yabancı anahtar özelliğine sahip olan varlıktır. Bir-çok ilişkisinde, ana/ü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 niteliğindeki 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/çocukların artık hiçbir asıl/ebeveyn ile ilişkili olmadığını gösterir. Bu, yabancı anahtarın boş olabilmesi gereken isteğe bağlı ilişkiler için varsayılan değerdir. Gerekli ilişkiler için FK'nin null olarak ayarlanması geçerli değildir, çünkü yabancı anahtar genellikle null atanamaz.
  2. Bağımlıları/çocukları silme. Bu, gerekli ilişkiler için varsayılandır ve isteğe bağlı ilişkiler için de geçerlidir.

Ayrıntılı bilgi için Yabancı Anahtarları ve Gezintileri Değiştirme kısmına bkz: Değişiklik izleme ve ilişkiler hakkında.

İsteğe bağlı ilişkiler

Kullandığımız modeldeki Post.BlogId yabancı anahtar özelliği null değer alabilir. 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. Örneğin:

// Attach a blog and associated posts
context.Attach(blog);

// Mark the blog as deleted
context.Remove(blog);

çağrıldıktan sonra Remove incelemek, beklendiği gibi blog'un Deleted şeklinde 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 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

Eğer Post.BlogId yabancı anahtar özelliği null değerlendirilemiyorsa, bloglar ve gönderiler arasındaki ilişki "gerekli" hale gelir. 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 ardından Remove incelemek, beklendiği gibi blogun tekrar Deleted olarak 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 Deleted olarak işaretlenmiş 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.

Uyarı

Bu belge, EF Core'daki ilişkilerle çalışmanın sadece yüzeyini hafifçe ele alır. İlişkiler hakkında daha fazla bilgi için ve SaveChanges'i çağırırken bağımlı/alt varlıkları güncelleştirme/silme hakkında daha fazla bilgi için Yabancı Anahtarları ve Gezintileri Değiştirme'ye bakın.

TrackGraph ile özel izleme

ChangeTracker.TrackGraph, Add, Attach ve Update gibi çalışır, ancak izlemeden önce her varlık örneği için bir geri çağırma oluşturur. 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ı, anahtar değeri negatif ise varlığın silinmesi gerektiğini söyleyecek şekilde uygulayalım. 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 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 Deleted olarak 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

Uyarı

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 kodlanabilir. 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, belirli bir varlıktan yeni ilgili varlıkları ziyaret etmeyi durdurur; bu, söz konusu varlık zaten izlendiğinde veya geri arama işlevi varlığı izlemeye başlamadığında gerçekleşir.

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 işlevi false döndürürse, grafik taraması 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 sunulmasına da olanak tanır ve bu durum daha sonra her geri çağırmaya aktarılır.