Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Monitor zmian platformy Entity Framework Core (EF Core) generuje dwa rodzaje danych wyjściowych, które ułatwiają debugowanie:
- Element ChangeTracker.DebugView zapewnia czytelny dla człowieka widok wszystkich śledzonych jednostek
- Komunikaty dziennika na poziomie debugowania są generowane, gdy monitor zmian wykrywa stan i naprawia relacje
Napiwek
W tym dokumencie przyjęto założenie, że stany jednostki i podstawy śledzenia zmian platformy EF Core są zrozumiałe. Aby uzyskać więcej informacji na temat tych tematów, zobacz Change Tracking in EF Core (Śledzenie zmian w programie EF Core ).
Napiwek
Możesz uruchomić i debugować cały kod podany w tym dokumencie, pobierając przykładowy kod z serwisu GitHub.
Widok debugowania śledzenia zmian
Dostęp do widoku debugowania monitora zmian można uzyskać w debugerze środowiska IDE. Na przykład w programie Visual Studio:
Dostęp do niego można również uzyskać bezpośrednio z kodu, na przykład w celu wysłania widoku debugowania do konsoli:
Console.WriteLine(context.ChangeTracker.DebugView.ShortView);
Widok debugowania ma krótką formę i długi formularz. W krótkim formularzu przedstawiono śledzone jednostki, ich stan i wartości klucza. Formularz długi zawiera również wszystkie wartości właściwości i nawigacji oraz stan.
Krótki widok
Przyjrzyjmy się przykładowi widoku debugowania przy użyciu modelu pokazanego na końcu tego dokumentu. Najpierw prześledzimy niektóre jednostki i umieścimy je w różnych stanach, więc mamy dobre dane śledzenia zmian w celu wyświetlenia:
using var context = new BlogsContext();
var blogs = await context.Blogs
.Include(e => e.Posts).ThenInclude(e => e.Tags)
.Include(e => e.Assets)
.ToListAsync();
// 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();
Drukowanie krótkiego widoku w tym momencie, jak pokazano powyżej, powoduje następujące dane wyjściowe:
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
Uwaga:
- Każda śledzona jednostka jest wyświetlana z wartością klucza podstawowego (PK). Na przykład
Blog {Id: 1}. - Jeśli jednostka jest typem jednostki typu współużytkowanego, jest również wyświetlany typ CLR. Na przykład
PostTag (Dictionary<string, object>). - Zostanie EntityState wyświetlony następny. Będzie to jeden z
Unchangedelementów ,Added,ModifiedlubDeleted. - Wartości dla wszystkich kluczy alternatywnych (AKs) są wyświetlane obok. Na przykład
AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}. - Na koniec są wyświetlane wartości dla wszystkich kluczy obcych (FKs). Na przykład
FK {PostsId: 4} FK {TagsId: 2}.
Widok długi
Długi widok można wysłać do konsoli w taki sam sposób jak w krótkim widoku:
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Dane wyjściowe dla tego samego stanu co powyższy krótki widok to:
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}]
Każda śledzona jednostka i jej stan są wyświetlane tak jak poprzednio. Jednak widok długi pokazuje również wartości właściwości i nawigacji.
Wartości właściwości
Dla każdej właściwości widok długi pokazuje, czy właściwość jest częścią klucza podstawowego (PK), klucza alternatywnego (AK) lub klucza obcego (FK). Na przykład:
-
Blog.Idjest właściwością klucza podstawowego:Id: 1 PK -
Blog.AssetsIdto właściwość klucza alternatywnego:AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK -
Post.BlogIdjest właściwością klucza obcego:BlogId: 2 FK -
BlogAssets.Idjest zarówno kluczem podstawowym, jak i właściwością klucza obcego:Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK
Wartości właściwości, które zostały zmodyfikowane, są oznaczone jako takie, a oryginalna wartość właściwości jest również wyświetlana. Na przykład Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'.
Na koniec jednostki z wartościami klucza tymczasowego wskazują, Added że wartość jest tymczasowa. Na przykład Id: -2147482643 PK Temporary.
Wartości nawigacji
Wartości nawigacji są wyświetlane przy użyciu wartości klucza podstawowego jednostek, do których odwołuje się nawigacja. Na przykład w danych wyjściowych powyżej wpis 3 jest związany z blogem 2. Oznacza to, że nawigacja Post.Blog wskazuje wystąpienie o identyfikatorze Blog 2. Jest to wyświetlane jako Blog: {Id: 2}.
Dzieje się tak samo w przypadku nawigacji kolekcji, z tą różnicą, że w tym przypadku może istnieć wiele powiązanych jednostek. Na przykład nawigacja kolekcji Blog.Posts zawiera trzy jednostki z wartościami klucza 1, 2 i -2147482643. Jest to wyświetlane jako [{Id: 1}, {Id: 2}, {Id: -2147482643}].
Rejestrowanie monitora zmian
Monitor zmian rejestruje komunikaty przy DebugLogLevel każdym wykryciu zmian właściwości lub nawigacji. Na przykład po ChangeTracker.DetectChanges() wywołaniu w kodzie w górnej części tego dokumentu i włączeniu rejestrowania debugowania są generowane następujące dzienniki:
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'.
Poniższa tabela zawiera podsumowanie komunikatów rejestrowania monitora zmian:
| Identyfikator zdarzenia | opis |
|---|---|
| CoreEventId.DetectChangesStarting | DetectChanges() jest uruchamiana |
| CoreEventId.DetectChangesCompleted | DetectChanges() ukończono |
| CoreEventId.PropertyChangeDetected | Zmieniono wartość właściwości normalnej |
| CoreEventId.ForeignKeyChangeDetected | Zmieniono wartość właściwości klucza obcego |
| CoreEventId.CollectionChangeDetected | Nawigacja po kolekcji bez pomijania ma dodane lub usunięte powiązane jednostki. |
| CoreEventId.ReferenceChangeDetected | Nawigacja referencyjna została zmieniona tak, aby wskazywała inną jednostkę lub ustawiono wartość null |
| CoreEventId.StartedTracking | Program EF Core rozpoczął śledzenie jednostki |
| CoreEventId.StateChanged | Element EntityState jednostki uległ zmianie |
| CoreEventId.ValueGenerated | Wartość została wygenerowana dla właściwości |
| CoreEventId.SkipCollectionChangeDetected | Nawigacja pominięcia kolekcji miała dodane lub usunięte jednostki pokrewne |
Model
Model używany w powyższych przykładach zawiera następujące typy jednostek:
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 jest w większości skonfigurowany zgodnie z konwencją, z zaledwie kilkoma wierszami w elem. 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);
}