Partilhar via


Eventos .NET no EF Core

Sugestão

Você pode baixar o exemplo de eventos do GitHub.

O Entity Framework Core (EF Core) expõe eventos do .NET que atuam como retornos de chamada quando certas coisas acontecem no código do EF Core. Os eventos são mais simples do que os intercetores e permitem um registo mais flexível. No entanto, eles são apenas sincronizados e, portanto, não podem executar E/S assíncronas 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 levantados pelo EF Core

Os seguintes eventos são levantados pelo EF Core:

Evento Quando levantado
DbContext.SavingChanges No início de SaveChanges ou SaveChangesAsync
DbContext.SavedChanges No final de um SaveChanges bem-sucedido 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 de estado de carimbo de data/hora

Cada entidade rastreada 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 Tracked eventos e StateChanged para detetar quando uma entidade muda de estado. Em seguida, carimba a entidade com a hora atual indicando quando essa alteração aconteceu. Isso resulta em carimbos de data/hora indicando 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 data e hora.

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

Um método no DbContext da aplicação pode definir timestamps para qualquer entidade que implemente esta 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 utilizado como um manipulador de eventos para ambos 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 aconteça no construtor de contexto.

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

Ambos os eventos são necessários porque novas entidades disparam eventos Tracked quando são acompanhadas pela primeira vez. StateChanged Os eventos são disparados apenas para entidades que mudam de estado enquanto estão sendo rastreadas.

O exemplo deste exemplo contém um aplicativo de console simples que faz alterações no banco de dados de blogs:

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 que estão acontecendo e os carimbos de data/hora que estão 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