Compartilhar via


Eventos do .NET no EF Core

Dica

Você pode baixar o exemplo de eventos do GitHub.

O EF Core (Entity Framework Core) expõe eventos do .NET para atuar como retornos de chamada quando certas ações ocorrem no código do EF Core. Os eventos são mais simples que interceptadores e permitem um registro mais flexível. No entanto, eles são apenas sincronizados e, portanto, não podem executar E/S assíncrona sem bloqueio.

Os eventos são registrados por DbContext instância. Use um ouvinte de diagnóstico para obter as mesmas informações, mas para todas as instâncias de DbContext no processo.

Eventos gerados pelo EF Core

Os seguintes eventos são gerados pelo EF Core:

Acontecimento Quando gerado
DbContext.SavingChanges No início de SaveChanges ou SaveChangesAsync
DbContext.SavedChanges No final de um bem-sucedido SaveChanges ou SaveChangesAsync
DbContext.SaveChangesFailed No final de uma falha SaveChanges ou SaveChangesAsync
ChangeTracker.Tracked Quando uma entidade é rastreada pelo contexto
ChangeTracker.StateChanged Quando uma entidade controlada altera seu estado

Exemplo: Alterações no estado da marca de tempo

Cada entidade controlada por um DbContext tem um EntityState. Por exemplo, o Added estado indica que a entidade será inserida no banco de dados.

Este exemplo usa os eventos Tracked e StateChanged para detectar quando uma entidade altera o estado. Em seguida, ele carimba a entidade com a hora atual indicando quando essa alteração aconteceu. Isso resulta em carimbos de data/hora que indicam quando a entidade foi inserida, excluída e/ou atualizada pela última vez.

Os tipos de entidade neste exemplo implementam uma interface que define as propriedades de marcação temporal.

public interface IHasTimestamps
{
    DateTime? Added { get; set; }
    DateTime? Deleted { get; set; }
    DateTime? Modified { get; set; }
}

Um método no DbContext do aplicativo pode definir carimbos de data/hora para qualquer entidade que implemente essa interface:

private static void UpdateTimestamps(object sender, EntityEntryEventArgs e)
{
    if (e.Entry.Entity is IHasTimestamps entityWithTimestamps)
    {
        switch (e.Entry.State)
        {
            case EntityState.Deleted:
                entityWithTimestamps.Deleted = DateTime.UtcNow;
                Console.WriteLine($"Stamped for delete: {e.Entry.Entity}");
                break;
            case EntityState.Modified:
                entityWithTimestamps.Modified = DateTime.UtcNow;
                Console.WriteLine($"Stamped for update: {e.Entry.Entity}");
                break;
            case EntityState.Added:
                entityWithTimestamps.Added = DateTime.UtcNow;
                Console.WriteLine($"Stamped for insert: {e.Entry.Entity}");
                break;
        }
    }
}

Esse método tem a assinatura apropriada para ser usado como um manipulador de eventos para os eventos Tracked e StateChanged. O manipulador é registrado para ambos os eventos no construtor DbContext. Observe que os eventos podem ser anexados a um DbContext a qualquer momento; não é necessário que isso ocorra no construtor de contexto.

public BlogsContext()
{
    ChangeTracker.StateChanged += UpdateTimestamps;
    ChangeTracker.Tracked += UpdateTimestamps;
}

Ambos os eventos são necessários porque novas entidades disparam Tracked eventos quando são rastreadas pela primeira vez. StateChanged os eventos são acionados apenas para entidades que alteram o estado enquanto estão sendo rastreados.

A amostra para este exemplo contém um aplicativo de console simples que faz alterações no banco de dados de blog:

using (var context = new BlogsContext())
{
    await context.Database.EnsureDeletedAsync();
    await context.Database.EnsureCreatedAsync();

    context.Add(
        new Blog
        {
            Id = 1,
            Name = "EF Blog",
            Posts = { new Post { Id = 1, Title = "EF Core 3.1!" }, new Post { Id = 2, Title = "EF Core 5.0!" } }
        });

    await context.SaveChangesAsync();
}

using (var context = new BlogsContext())
{
    var blog = await context.Blogs.Include(e => e.Posts).SingleAsync();

    blog.Name = "EF Core Blog";
    context.Remove(blog.Posts.First());
    blog.Posts.Add(new Post { Id = 3, Title = "EF Core 6.0!" });

    await context.SaveChangesAsync();
}

A saída desse código mostra as alterações de estado acontecendo e os carimbos de data/hora sendo aplicados:

Stamped for insert: Blog 1 Added on: 10/15/2020 11:01:26 PM
Stamped for insert: Post 1 Added on: 10/15/2020 11:01:26 PM
Stamped for insert: Post 2 Added on: 10/15/2020 11:01:26 PM
Stamped for delete: Post 1 Added on: 10/15/2020 11:01:26 PM Deleted on: 10/15/2020 11:01:26 PM
Stamped for update: Blog 1 Added on: 10/15/2020 11:01:26 PM Modified on: 10/15/2020 11:01:26 PM
Stamped for insert: Post 3 Added on: 10/15/2020 11:01:26 PM