Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O controlador de alterações do Entity Framework Core (EF Core) gera dois tipos de saída para ajudar na depuração:
- O ChangeTracker.DebugView fornece uma visão legível por humanos de todas as entidades que estão sendo rastreadas
- As mensagens de log ao nível de depuração são geradas quando o controlador de alterações deteta o estado e corrige as relações.
Sugestão
Este documento pressupõe que os estados da entidade e os conceitos básicos do controle de alterações do EF Core sejam compreendidos. Consulte Controle de alterações no EF Core para obter mais informações sobre esses tópicos.
Sugestão
Você pode executar e depurar todo o código neste documento baixando o código de exemplo do GitHub.
Alterar a visualização de depuração do rastreador
A visualização de depuração do controlador de alterações pode ser acessada no depurador do seu IDE. Por exemplo, com o Visual Studio:
Ele também pode ser acessado diretamente do código, por exemplo, para enviar a visualização de depuração para o console:
Console.WriteLine(context.ChangeTracker.DebugView.ShortView);
A visualização de depuração tem um formulário curto e um formulário longo. O formulário curto mostra entidades controladas, seu estado e valores-chave. O formulário longo também inclui todos os valores e estados de propriedade e navegação.
A vista curta
Vejamos um exemplo de visão de depuração usando o modelo mostrado no final deste documento. Primeiro, vamos rastrear algumas entidades e colocá-las em alguns estados diferentes, apenas para que tenhamos bons dados de controle de alterações para visualizar:
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();
Imprimir a visualização curta neste ponto, como mostrado acima, resulta na seguinte saída:
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
Aviso:
- Cada entidade rastreada é listada com seu valor de chave primária (PK). Por exemplo,
Blog {Id: 1}. - Se a entidade for um tipo de entidade de tipo compartilhado, seu tipo CLR também será mostrado. Por exemplo,
PostTag (Dictionary<string, object>). - O EntityState é mostrado a seguir. Este será um dos
Unchanged,Added,Modified, ouDeleted. - Os valores de quaisquer chaves alternativas (AKs) são mostrados a seguir. Por exemplo,
AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}. - Finalmente, os valores para quaisquer chaves estrangeiras (FKs) são exibidos. Por exemplo,
FK {PostsId: 4} FK {TagsId: 2}.
A visão de longo prazo
A vista longa pode ser enviada para a consola da mesma forma que a vista curta:
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
A saída para o mesmo estado que a visualização curta acima é:
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}]
Cada entidade rastreada e seu estado são mostrados como antes. No entanto, a visão ampla também exibe valores de propriedade e navegação.
Valores de propriedade
Para cada propriedade, a visualização longa mostra se a propriedade faz ou não parte de uma chave primária (PK), chave alternativa (AK) ou chave estrangeira (FK). Por exemplo:
-
Blog.Idé uma propriedade de chave primária:Id: 1 PK -
Blog.AssetsIdé uma propriedade de chave alternativa:AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK -
Post.BlogIdé uma propriedade de chave estrangeira:BlogId: 2 FK -
BlogAssets.Idé uma chave primária e uma propriedade de chave estrangeira:Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK
Os valores de propriedade que foram modificados são marcados como tal, e o valor original da propriedade também é mostrado. Por exemplo, Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'.
Finalmente, Added entidades com valores de chave temporários indicam que o valor é temporário. Por exemplo, Id: -2147482643 PK Temporary.
Valores de navegação
Os valores de navegação são exibidos usando os valores de chave primária das entidades às quais as navegações fazem referência. Por exemplo, na saída acima, o post 3 está relacionado ao blog 2. Isso significa que a Post.Blog navegação aponta para a Blog instância com ID 2. Isso é mostrado como Blog: {Id: 2}.
A mesma coisa acontece para navegações de coleção, exceto que, neste caso, pode haver várias entidades relacionadas. Por exemplo, a navegação Blog.Posts da coleção contém três entidades, com valores de chave 1, 2 e -2147482643 respectivamente. Isso é mostrado como [{Id: 1}, {Id: 2}, {Id: -2147482643}].
Registo do controlador de alterações
O rastreio de alterações regista mensagens no/a DebugLogLevel sempre que deteta alterações de propriedade ou navegação. Por exemplo, quando ChangeTracker.DetectChanges() é chamado no código na parte superior deste documento e os registos de depuração estão ativados, os seguintes registos são gerados:
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 tabela a seguir resume as mensagens de log do controlador de alterações:
| ID do Evento | Descrição |
|---|---|
| CoreEventId.DetectChangesStarting | DetectChanges() está a começar |
| CoreEventId.DetectChangesCompleted | DetectChanges() concluiu |
| CoreEventId.PropertyChangeDetected | Um valor de propriedade normal foi alterado |
| CoreEventId.ForeignKeyChangeDetected | O valor de uma propriedade de chave estrangeira foi alterado |
| CoreEventId.CollectionChangeDetected | Uma navegação de coleção que não pode ser ignorada teve entidades relacionadas adicionadas ou removidas. |
| CoreEventId.ReferenceChangeDetected | Uma navegação de referência foi alterada para apontar para outra entidade ou definida como null |
| CoreEventId.StartedTracking | O EF Core começou a rastrear uma entidade |
| CoreEventId.StateChanged | O EntityState de uma entidade mudou |
| CoreEventId.ValueGenerated | Foi gerado um valor para uma propriedade |
| CoreEventId.SkipCollectionChangeDetected | Uma navegação de coleção skip teve entidades relacionadas adicionadas ou removidas |
O modelo
O modelo usado para os exemplos acima contém os seguintes tipos de entidade:
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
}
O modelo é configurado principalmente por convenção, com apenas algumas linhas em 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);
}