Penelusuran Kesalahan Pelacak Perubahan
Pelacak perubahan Entity Framework Core (EF Core) menghasilkan dua jenis output untuk membantu penelusuran kesalahan:
- memberikan ChangeTracker.DebugView tampilan yang dapat dibaca manusia dari semua entitas yang dilacak
- Pesan log tingkat debug dihasilkan saat pelacak perubahan mendeteksi status dan memperbaiki hubungan
Tip
Dokumen ini mengasumsikan bahwa status entitas dan dasar-dasar pelacakan perubahan EF Core dipahami. Lihat Pelacakan Perubahan di EF Core untuk informasi selengkapnya tentang topik ini.
Tip
Anda dapat menjalankan dan men-debug ke semua kode dalam dokumen ini dengan mengunduh kode sampel dari GitHub.
Mengubah tampilan debug pelacak
Tampilan debug pelacak perubahan dapat diakses di debugger IDE Anda. Misalnya, dengan Visual Studio:
Ini juga dapat diakses langsung dari kode, misalnya untuk mengirim tampilan debug ke konsol:
Console.WriteLine(context.ChangeTracker.DebugView.ShortView);
Tampilan debug memiliki bentuk pendek dan bentuk panjang. Formulir pendek menunjukkan entitas terlacak, statusnya, dan nilai kuncinya. Formulir panjang juga mencakup semua nilai dan status properti dan navigasi.
Tampilan pendek
Mari kita lihat contoh tampilan debug menggunakan model yang ditampilkan di akhir dokumen ini. Pertama, kami akan melacak beberapa entitas dan menempatkannya di beberapa status yang berbeda, sehingga kami memiliki data pelacakan perubahan yang baik untuk dilihat:
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();
Mencetak tampilan pendek pada titik ini, seperti yang ditunjukkan di atas, menghasilkan output berikut:
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
Pemberitahuan:
- Setiap entitas yang dilacak tercantum dengan nilai kunci primer (PK). Contohnya,
Blog {Id: 1}
. - Jika entitas adalah jenis entitas jenis bersama, maka jenis CLR juga ditampilkan. Contohnya,
PostTag (Dictionary<string, object>)
. - selanjutnya EntityState ditunjukkan. Ini akan menjadi salah satu dari
Unchanged
,Added
,Modified
, atauDeleted
. - Nilai untuk kunci alternatif (AK) ditampilkan berikutnya. Contohnya,
AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}
. - Terakhir, nilai untuk setiap kunci asing (FK) ditampilkan. Contohnya,
FK {PostsId: 4} FK {TagsId: 2}
.
Tampilan panjang
Tampilan panjang dapat dikirim ke konsol dengan cara yang sama seperti tampilan singkat:
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Output untuk status yang sama dengan tampilan singkat di atas adalah:
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}]
Setiap entitas terlacak dan statusnya ditampilkan seperti sebelumnya. Namun, tampilan panjang juga memperlihatkan nilai properti dan navigasi.
Nilai properti
Untuk setiap properti, tampilan panjang menunjukkan apakah properti adalah bagian dari kunci primer (PK), kunci alternatif (AK), atau kunci asing (FK) atau tidak. Contohnya:
Blog.Id
adalah properti kunci utama:Id: 1 PK
Blog.AssetsId
adalah properti kunci alternatif:AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
Post.BlogId
adalah properti kunci asing:BlogId: 2 FK
BlogAssets.Id
adalah kunci primer dan properti kunci asing:Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK
Nilai properti yang telah dimodifikasi ditandai seperti itu, dan nilai asli properti juga ditampilkan. Contohnya,Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'
.
Terakhir, Added
entitas dengan nilai kunci sementara menunjukkan bahwa nilainya bersifat sementara. Contohnya,Id: -2147482643 PK Temporary
.
Nilai navigasi
Nilai navigasi ditampilkan menggunakan nilai kunci utama entitas yang direferensikan navigasi. Misalnya, dalam output di atas, posting 3 terkait dengan blog 2. Ini berarti bahwa Post.Blog
navigasi menunjuk ke Blog
instans dengan ID 2. Ini ditampilkan sebagai Blog: {Id: 2}
.
Hal yang sama terjadi untuk navigasi pengumpulan, kecuali dalam hal ini mungkin ada beberapa entitas terkait. Misalnya, navigasi Blog.Posts
koleksi berisi tiga entitas, dengan nilai kunci masing-masing 1, 2, dan -2147482643. Ini ditampilkan sebagai [{Id: 1}, {Id: 2}, {Id: -2147482643}]
.
Mengubah pengelogan pelacak
Pelacak perubahan mencatat pesan pada Debug
LogLevel setiap kali mendeteksi properti atau navigasi berubah. Misalnya, ketika ChangeTracker.DetectChanges() dipanggil dalam kode di bagian atas dokumen ini dan pengelogan debug diaktifkan, maka log berikut dihasilkan:
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'.
Tabel berikut ini merangkum pesan pengelogan pelacak perubahan:
ID Peristiwa | Deskripsi |
---|---|
CoreEventId.DetectChangesStarting | DetectChanges() sedang dimulai |
CoreEventId.DetectChangesCompleted | DetectChanges() telah selesai |
CoreEventId.PropertyChangeDetected | Nilai properti normal telah berubah |
CoreEventId.ForeignKeyChangeDetected | Nilai properti kunci asing telah berubah |
CoreEventId.CollectionChangeDetected | Navigasi pengumpulan yang tidak dilewati telah menambahkan atau menghapus entitas terkait. |
CoreEventId.ReferenceChangeDetected | Navigasi referensi telah diubah untuk menunjuk ke entitas lain, atau diatur ke null |
CoreEventId.StartedTracking | EF Core mulai melacak entitas |
CoreEventId.StateChanged | Entitas EntityState telah berubah |
CoreEventId.ValueGenerated | Nilai dihasilkan untuk properti |
CoreEventId.SkipCollectionChangeDetected | Navigasi kumpulan lewati telah menambahkan atau menghapus entitas terkait |
Model
Model yang digunakan untuk contoh di atas berisi jenis entitas berikut:
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 ini sebagian besar dikonfigurasi oleh konvensi, hanya dengan beberapa baris di OnModelCreating:
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);
}