Delen via


Foutopsporing voor trackers wijzigen

Met de change tracker van Entity Framework Core (EF Core) worden twee soorten uitvoer gegenereerd om u te helpen bij het opsporen van fouten:

  • Het ChangeTracker.DebugView biedt een door mensen leesbare weergave van alle entiteiten die worden bijgehouden
  • Logboekberichten op foutopsporingsniveau worden gegenereerd wanneer de wijzigingstracker de status detecteert en relaties oplost

Aanbeveling

In dit document wordt ervan uitgegaan dat entiteitsstatussen en de basisprincipes van EF Core-wijzigingen bijhouden worden begrepen. Zie Wijzigingen bijhouden in EF Core voor meer informatie over deze onderwerpen.

Aanbeveling

U kunt alle code in dit document uitvoeren en fouten opsporen door de voorbeeldcode van GitHub te downloaden.

Foutopsporingsweergave voor trackers wijzigen

De foutopsporingsweergave voor wijzigingentrackers kan worden geopend in het foutopsporingsprogramma van uw IDE. Bijvoorbeeld met Visual Studio:

Toegang tot de foutopsporingsweergave voor wijzigingentrackers vanuit het Foutopsporingsprogramma van Visual Studio

Deze kan ook rechtstreeks vanuit code worden geopend, bijvoorbeeld om de foutopsporingsweergave naar de console te verzenden:

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

De foutopsporingsweergave heeft een kort formulier en een lang formulier. In het korte formulier worden bijgehouden entiteiten, hun status en sleutelwaarden weergegeven. Het lange formulier bevat ook alle eigenschaps- en navigatiewaarden en -status.

De korte weergave

Laten we eens kijken naar een voorbeeld van een foutopsporingsweergave met behulp van het model dat aan het einde van dit document wordt weergegeven. Eerst volgen we een paar entiteiten en plaatsen we ze in een aantal verschillende staten, zodat we goede veranderingsgegevens bijhouden om te bekijken.

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();

Het afdrukken van de korte weergave op dit punt, zoals hierboven wordt weergegeven, resulteert in de volgende uitvoer:

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

Kennisgeving:

  • Elke bijgehouden entiteit wordt weergegeven met de waarde van de primaire sleutel (PK). Bijvoorbeeld: Blog {Id: 1}.
  • Als de entiteit een type gedeelde entiteit is, wordt het CLR-type ook weergegeven. Bijvoorbeeld: PostTag (Dictionary<string, object>).
  • De volgende EntityState wordt weergegeven. Dit is een vanUnchanged, Added, of ModifiedDeleted.
  • Waarden voor alternatieve sleutels (AK's) worden hierna weergegeven. Bijvoorbeeld: AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}.
  • Ten slotte worden waarden voor refererende sleutels (FK's) weergegeven. Bijvoorbeeld: FK {PostsId: 4} FK {TagsId: 2}.

Het lange termijnperspectief

De lange weergave kan op dezelfde manier naar de console worden verzonden als de korte weergave:

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

De uitvoer voor dezelfde status als de bovenstaande korte weergave is:

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}]

Elke bijgehouden entiteit en de status ervan worden weergegeven zoals voorheen. In de lange weergave worden echter ook eigenschaps- en navigatiewaarden weergegeven.

Vastgoedwaarden

Voor elke eigenschap geeft de lange weergave aan of de eigenschap deel uitmaakt van een primaire sleutel (PK), alternatieve sleutel (AK) of refererende sleutel (FK). Voorbeeld:

  • Blog.Id is een primaire sleuteleigenschap: Id: 1 PK
  • Blog.AssetsId is een alternatieve sleuteleigenschap: AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
  • Post.BlogId is een vreemde-sleutel eigenschap: BlogId: 2 FK
  • BlogAssets.Id is zowel een primaire sleutel als een vreemde sleutel: Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK

Eigenschapswaarden die zijn gewijzigd, worden als zodanig gemarkeerd en de oorspronkelijke waarde van de eigenschap wordt ook weergegeven. Bijvoorbeeld: Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'.

Ten slotte Added geven entiteiten met tijdelijke sleutelwaarden aan dat de waarde tijdelijk is. Bijvoorbeeld: Id: -2147482643 PK Temporary.

Navigatiewaarden worden weergegeven met behulp van de primaire sleutelwaarden van de entiteiten waarnaar wordt verwezen door de navigatie. In de bovenstaande uitvoer is post 3 bijvoorbeeld gerelateerd aan blog 2. Dit betekent dat de Post.Blog navigatie naar het Blog exemplaar verwijst met id 2. Dit wordt weergegeven als Blog: {Id: 2}.

Hetzelfde gebeurt voor verzamelingsnavigatie, behalve dat er in dit geval meerdere gerelateerde entiteiten kunnen zijn. De verzamelingsnavigatie Blog.Posts bevat bijvoorbeeld drie entiteiten, met respectievelijk sleutelwaarden 1, 2 en -2147482643. Dit wordt weergegeven als [{Id: 1}, {Id: 2}, {Id: -2147482643}].

Logboekregistratie van tracker wijzigen

De wijzigingentracker registreert berichten bij de DebugLogLevel zodra het wijzigingen van eigenschap of navigatie detecteert. Wanneer de code boven ChangeTracker.DetectChanges() aan dit document wordt aangeroepen en logboekregistratie voor foutopsporing is ingeschakeld, worden de volgende logboeken gegenereerd:

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'.

De volgende tabel bevat een samenvatting van de logboekberichten voor wijzigingentrackers:

Gebeurtenis-id Beschrijving
CoreEventId.DetectChangesStarting DetectChanges() begint
CoreEventId.DetectChangesCompleted DetectChanges() is voltooid
CoreEventId.PropertyChangeDetected Een normale eigenschapswaarde is gewijzigd
CoreEventId.ForeignKeyChangeDetected Een waarde voor de eigenschap van een foreign key is gewijzigd
CoreEventId.CollectionChangeDetected Bij een verzamelingsnavigatie zonder skip-optie zijn gerelateerde entiteiten toegevoegd of verwijderd.
CoreEventId.ReferenceChangeDetected Een verwijzingsnavigatie is gewijzigd zodat deze verwijst naar een andere entiteit of is ingesteld op null
CoreEventId.StartedTracking EF Core is begonnen met het bijhouden van een entiteit
CoreEventId.StateChanged Het EntityState van een entiteit is gewijzigd
CoreEventId.ValueGenerated Er is een waarde gegenereerd voor een eigenschap
CoreEventId.SkipCollectionChangeDetected Een overslaan van verzamelnavigatie heeft gerelateerde entiteiten toegevoegd of verwijderd

Het model

Het model dat wordt gebruikt voor de bovenstaande voorbeelden bevat de volgende entiteitstypen:

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
}

Het model is meestal geconfigureerd volgens de conventie, met slechts een paar regels in 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);
}