İzleyici Hata Ayıklamasını Değiştir

Entity Framework Core (EF Core) değişiklik izleyicisi, hata ayıklamaya yardımcı olmak için iki tür çıkış oluşturur:

  • , ChangeTracker.DebugView izlenen tüm varlıkların okunabilir bir görünümünü sağlar
  • Hata ayıklama düzeyi günlük iletileri, değişiklik izleyicisi durumu algıladığında ve ilişkileri düzelttiğinde oluşturulur

İpucu

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.

İpucu

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

İzleyici hata ayıklama görünümünü değiştirme

Değişiklik izleyicisi hata ayıklama görünümüne IDE'nizin hata ayıklayıcısında erişilebilir. Örneğin, Visual Studio ile:

Visual Studio hata ayıklayıcısından değişiklik izleyicisi hata ayıklama görünümüne erişme

Ayrıca doğrudan koddan da erişilebilir, örneğin hata ayıklama görünümünü konsola göndermek için:

Console.WriteLine(context.ChangeTracker.DebugView.ShortView);

Hata ayıklama görünümünün kısa ve uzun bir formu vardır. Kısa form izlenen varlıkları, bunların durumunu ve anahtar değerlerini gösterir. Uzun form ayrıca tüm özellik ve gezinti değerlerini ve durumunu da içerir.

Kısa görünüm

Şimdi bu belgenin sonunda gösterilen modeli kullanarak hata ayıklama görünümü örneğine bakalım. İlk olarak, bazı varlıkları izleyeceğiz ve bazı farklı durumlara yerleştireceğiz, böylece görüntülemek için iyi değişiklik izleme verilerine sahip olacağız:

using var context = new BlogsContext();

var blogs = context.Blogs
    .Include(e => e.Posts).ThenInclude(e => e.Tags)
    .Include(e => e.Assets)
    .ToList();

// Mark something Added
blogs[0].Posts.Add(
    new Post
    {
        Title = "What’s next for System.Text.Json?",
        Content = ".NET 5.0 was released recently and has come with many new features and..."
    });

// Mark something Deleted
blogs[1].Posts.Remove(blogs[1].Posts[1]);

// Make something Modified
blogs[0].Name = ".NET Blog (All new!)";

context.ChangeTracker.DetectChanges();

Bu noktada, yukarıda gösterildiği gibi kısa görünümün yazdırılıyorsa aşağıdaki çıkış elde edilir:

Blog {Id: 1} Modified AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}
Blog {Id: 2} Unchanged AK {AssetsId: 3a54b880-2b9d-486b-9403-dc2e52d36d65}
BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged FK {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65}
BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged FK {Id: ed727978-1ffe-4709-baee-73913e8e44a0}
Post {Id: -2147482643} Added FK {BlogId: 1}
Post {Id: 1} Unchanged FK {BlogId: 1}
Post {Id: 2} Unchanged FK {BlogId: 1}
Post {Id: 3} Unchanged FK {BlogId: 2}
Post {Id: 4} Deleted FK {BlogId: 2}
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 1} Unchanged FK {PostsId: 1} FK {TagsId: 1}
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 3} Unchanged FK {PostsId: 1} FK {TagsId: 3}
PostTag (Dictionary<string, object>) {PostsId: 2, TagsId: 1} Unchanged FK {PostsId: 2} FK {TagsId: 1}
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 2} Unchanged FK {PostsId: 3} FK {TagsId: 2}
PostTag (Dictionary<string, object>) {PostsId: 4, TagsId: 2} Deleted FK {PostsId: 4} FK {TagsId: 2}
Tag {Id: 1} Unchanged
Tag {Id: 2} Unchanged
Tag {Id: 3} Unchanged

Not:

  • İzlenen her varlık birincil anahtar (PK) değeriyle listelenir. Örneğin, Blog {Id: 1}.
  • Varlık paylaşılan türde bir varlık türüyse CLR türü de gösterilir. Örneğin, PostTag (Dictionary<string, object>).
  • EntityState daha sonra gösterilir. Bu , , AddedModifiedveya DeletedolacaktırUnchanged.
  • Bundan sonra herhangi bir alternatif anahtar (AK) için değerler gösterilir. Örneğin, AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}.
  • Son olarak, herhangi bir yabancı anahtar (FK) için değerler gösterilir. Örneğin, FK {PostsId: 4} FK {TagsId: 2}.

Uzun görünüm

Uzun görünüm, kısa görünümle aynı şekilde konsola gönderilebilir:

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

Yukarıdaki kısa görünümle aynı durum için çıkış şu şekildedir:

Blog {Id: 1} Modified
  Id: 1 PK
  AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
  Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'
  Assets: {Id: ed727978-1ffe-4709-baee-73913e8e44a0}
  Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Blog {Id: 2} Unchanged
  Id: 2 PK
  AssetsId: '3a54b880-2b9d-486b-9403-dc2e52d36d65' AK
  Name: 'Visual Studio Blog'
  Assets: {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65}
  Posts: [{Id: 3}]
BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged
  Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK
  Banner: <null>
  Blog: {Id: 2}
BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged
  Id: 'ed727978-1ffe-4709-baee-73913e8e44a0' PK FK
  Banner: <null>
  Blog: {Id: 1}
Post {Id: -2147482643} Added
  Id: -2147482643 PK Temporary
  BlogId: 1 FK
  Content: '.NET 5.0 was released recently and has come with many new fe...'
  Title: 'What's next for System.Text.Json?'
  Blog: {Id: 1}
  Tags: []
Post {Id: 1} Unchanged
  Id: 1 PK
  BlogId: 1 FK
  Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
  Title: 'Announcing the Release of EF Core 5.0'
  Blog: {Id: 1}
  Tags: [{Id: 1}, {Id: 3}]
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 1 FK
  Content: 'F# 5 is the latest version of F#, the functional programming...'
  Title: 'Announcing F# 5'
  Blog: {Id: 1}
  Tags: [{Id: 1}]
Post {Id: 3} Unchanged
  Id: 3 PK
  BlogId: 2 FK
  Content: 'If you are focused on squeezing out the last bits of perform...'
  Title: 'Disassembly improvements for optimized managed debugging'
  Blog: {Id: 2}
  Tags: [{Id: 2}]
Post {Id: 4} Deleted
  Id: 4 PK
  BlogId: 2 FK
  Content: 'Examine when database queries were executed and measure how ...'
  Title: 'Database Profiling with Visual Studio'
  Blog: <null>
  Tags: [{Id: 2}]
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 1} Unchanged
  PostsId: 1 PK FK
  TagsId: 1 PK FK
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 3} Unchanged
  PostsId: 1 PK FK
  TagsId: 3 PK FK
PostTag (Dictionary<string, object>) {PostsId: 2, TagsId: 1} Unchanged
  PostsId: 2 PK FK
  TagsId: 1 PK FK
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 2} Unchanged
  PostsId: 3 PK FK
  TagsId: 2 PK FK
PostTag (Dictionary<string, object>) {PostsId: 4, TagsId: 2} Deleted
  PostsId: 4 PK FK
  TagsId: 2 PK FK
Tag {Id: 1} Unchanged
  Id: 1 PK
  Text: '.NET'
  Posts: [{Id: 1}, {Id: 2}]
Tag {Id: 2} Unchanged
  Id: 2 PK
  Text: 'Visual Studio'
  Posts: [{Id: 3}, {Id: 4}]
Tag {Id: 3} Unchanged
  Id: 3 PK
  Text: 'EF Core'
  Posts: [{Id: 1}]

İzlenen her varlık ve durumu daha önce olduğu gibi gösterilir. Ancak, uzun görünüm özellik ve gezinti değerlerini de gösterir.

Özellik değerleri

Her özellik için uzun görünüm özelliğin birincil anahtar (PK), alternatif anahtar (AK) veya yabancı anahtar (FK) parçası olup olmadığını gösterir. Örneğin:

  • Blog.Id birincil anahtar özelliğidir: Id: 1 PK
  • Blog.AssetsId alternatif bir anahtar özelliğidir: AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
  • Post.BlogId yabancı anahtar özelliğidir: BlogId: 2 FK
  • BlogAssets.Id hem birincil anahtar hem de yabancı anahtar özelliğidir: Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK

Değiştirilen özellik değerleri bu şekilde işaretlenir ve özelliğin özgün değeri de gösterilir. Örneğin, Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'.

Son olarak, Added geçici anahtar değerlerine sahip varlıklar değerin geçici olduğunu gösterir. Örneğin, Id: -2147482643 PK Temporary.

Gezinti değerleri, gezintilerin başvurduğunu varlıkların birincil anahtar değerleri kullanılarak görüntülenir. Örneğin, yukarıdaki çıktıda, 3 gönderisi blog 2 ile ilgilidir. Bu, gezintinin Post.Blog 2 kimliğine Blog sahip örneğe işaret ettiğini gösterir. Bu, olarak Blog: {Id: 2}gösterilir.

Koleksiyon gezintilerinde de aynı şey olur, ancak bu durumda birden çok ilişkili varlık olabilir. Örneğin, koleksiyon gezintisi Blog.Posts sırasıyla 1, 2 ve -2147482643 anahtar değerlerine sahip üç varlık içerir. Bu, olarak [{Id: 1}, {Id: 2}, {Id: -2147482643}]gösterilir.

İzleyici günlüğünü değiştirme

Değişiklik izleyicisi, özellik veya gezinti değişikliklerini algılasa iletileri 'de günlüğe Debug LogLevel kaydeder. Örneğin, bu belgenin en üstündeki kodda çağrıldığında ChangeTracker.DetectChanges() ve hata ayıklama günlüğü etkinleştirildiğinde aşağıdaki günlükler oluşturulur:

dbug: 12/30/2020 13:52:44.815 CoreEventId.DetectChangesStarting[10800] (Microsoft.EntityFrameworkCore.ChangeTracking)
      DetectChanges starting for 'BlogsContext'.
dbug: 12/30/2020 13:52:44.818 CoreEventId.PropertyChangeDetected[10802] (Microsoft.EntityFrameworkCore.ChangeTracking)
      The unchanged property 'Blog.Name' was detected as changed from '.NET Blog' to '.NET Blog (All new!)' and will be marked as modified for entity with key '{Id: 1}'.
dbug: 12/30/2020 13:52:44.820 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking)
      The 'Blog' entity with key '{Id: 1}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Modified'.
dbug: 12/30/2020 13:52:44.821 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking)
      1 entities were added and 0 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 1}'.
dbug: 12/30/2020 13:52:44.822 CoreEventId.ValueGenerated[10808] (Microsoft.EntityFrameworkCore.ChangeTracking)
      'BlogsContext' generated temporary value '-2147482638' for the property 'Id.Post'.
dbug: 12/30/2020 13:52:44.822 CoreEventId.StartedTracking[10806] (Microsoft.EntityFrameworkCore.ChangeTracking)
      Context 'BlogsContext' started tracking 'Post' entity with key '{Id: -2147482638}'.
dbug: 12/30/2020 13:52:44.827 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking)
      0 entities were added and 1 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 2}'.
dbug: 12/30/2020 13:52:44.827 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking)
      The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Modified'.
dbug: 12/30/2020 13:52:44.829 CoreEventId.CascadeDeleteOrphan[10003] (Microsoft.EntityFrameworkCore.Update)
      An entity of type 'Post' with key '{Id: 4}' changed to 'Deleted' state due to severed required relationship to its parent entity of type 'Blog'.
dbug: 12/30/2020 13:52:44.829 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking)
      The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Modified' to 'Deleted'.
dbug: 12/30/2020 13:52:44.829 CoreEventId.CollectionChangeDetected[10804] (Microsoft.EntityFrameworkCore.ChangeTracking)
      0 entities were added and 1 entities were removed from navigation 'Blog.Posts' on entity with key '{Id: 2}'.
dbug: 12/30/2020 13:52:44.831 CoreEventId.CascadeDelete[10002] (Microsoft.EntityFrameworkCore.Update)
      A cascade state change of an entity of type 'PostTag' with key '{PostsId: 4, TagsId: 2}' to 'Deleted' occurred due to the deletion of its parent entity of type 'Post' with key '{Id: 4}'.
dbug: 12/30/2020 13:52:44.831 CoreEventId.StateChanged[10807] (Microsoft.EntityFrameworkCore.ChangeTracking)
      The 'PostTag' entity with key '{PostsId: 4, TagsId: 2}' tracked by 'BlogsContext' changed state from 'Unchanged' to 'Deleted'.
dbug: 12/30/2020 13:52:44.831 CoreEventId.DetectChangesCompleted[10801] (Microsoft.EntityFrameworkCore.ChangeTracking)
      DetectChanges completed for 'BlogsContext'.

Aşağıdaki tabloda değişiklik izleyicisi günlük iletileri özetlenmektedir:

Olay Kimliği Açıklama
CoreEventId.DetectChangesStarting DetectChanges() başlatılıyor
CoreEventId.DetectChangesCompleted DetectChanges() Tamamlan
CoreEventId.PropertyChangeDetected Normal özellik değeri değişti
CoreEventId.ForeignKeyChangeDetected Yabancı anahtar özellik değeri değişti
CoreEventId.CollectionChangeDetected Atlama olmayan bir koleksiyon gezintisinde ilgili varlıklar eklendi veya kaldırıldı.
CoreEventId.ReferenceChangeDetected Başvuru gezintisi başka bir varlığa işaret etmek için değiştirildi veya null olarak ayarlandı
CoreEventId.StartedTracking EF Core varlığı izlemeye başladı
CoreEventId.StateChanged Bir EntityState varlığın türü değişti
CoreEventId.ValueGenerated Bir özellik için bir değer oluşturuldu
CoreEventId.SkipCollectionChangeDetected Atlama koleksiyonu gezintisinde ilgili varlıklar eklendi veya kaldırıldı

Model

Yukarıdaki örnekler için kullanılan model aşağıdaki varlık türlerini içerir:

public class Blog
{
    public int Id { get; set; } // Primary key
    public Guid AssetsId { get; set; } // Alternate key
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>(); // Collection navigation
    public BlogAssets Assets { get; set; } // Reference navigation
}

public class BlogAssets
{
    public Guid Id { get; set; } // Primary key and foreign key
    public byte[] Banner { get; set; }

    public Blog Blog { get; set; } // Reference navigation
}

public class Post
{
    public int Id { get; set; } // Primary key
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; } // Foreign key
    public Blog Blog { get; set; } // Reference navigation

    public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation
}

public class Tag
{
    public int Id { get; set; } // Primary key
    public string Text { get; set; }

    public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation
}

Model çoğunlukla kurala göre yapılandırılır ve OnModelCreating'de yalnızca birkaç satır bulunur:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .Property(e => e.AssetsId)
        .ValueGeneratedOnAdd();

    modelBuilder
        .Entity<BlogAssets>()
        .HasOne(e => e.Blog)
        .WithOne(e => e.Assets)
        .HasForeignKey<BlogAssets>(e => e.Id)
        .HasPrincipalKey<Blog>(e => e.AssetsId);
}