Compartir a través de


Eventos de .NET en EF Core

Sugerencia

Puede descargar el ejemplo de eventos de GitHub.

Entity Framework Core (EF Core) expone eventos de .NET para que actúen como devoluciones de llamada cuando se producen ciertas cosas en el código de EF Core. Los eventos son más sencillos que los interceptores y permiten un registro más flexible. Sin embargo, solo se sincronizan y, por tanto, no pueden realizar E/S asincrónicas no bloqueantes.

Los eventos se registran por DbContext instancia. Use un escuchador de diagnóstico para obtener la misma información, pero para todas las instancias de DbContext del proceso.

Eventos generados por EF Core

EF Core genera los siguientes eventos:

Evento Cuando se levanta
DbContext.SavingChanges Al principio de SaveChanges o SaveChangesAsync
DbContext.SavedChanges Al final de una operación correcta SaveChanges o SaveChangesAsync
DbContext.SaveChangesFailed Al final de un error SaveChanges o SaveChangesAsync
ChangeTracker.Tracked Cuando el contexto realiza un seguimiento de una entidad
ChangeTracker.StateChanged Cuando una entidad con seguimiento cambia su estado

Ejemplo: cambios en el estado del registro de tiempo

Cada entidad de la que realiza un seguimiento dbContext tiene un EntityState. Por ejemplo, el Added estado indica que la entidad se insertará en la base de datos.

En este ejemplo se usan los Tracked eventos y StateChanged para detectar cuándo cambia el estado de una entidad. A continuación, marca la entidad con la hora actual que indica cuándo se produjo este cambio. Esto da como resultado marcas de tiempo que indican cuándo se insertó, eliminó o actualizó por última vez la entidad.

Los tipos de entidad de este ejemplo implementan una interfaz que define las propiedades de marca de tiempo:

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

A continuación, un método de DbContext de la aplicación puede establecer marcas de tiempo para cualquier entidad que implemente esta interfaz:

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

Este método tiene la firma adecuada para usar como controlador de eventos para los Tracked eventos y StateChanged . El controlador se registra para ambos eventos en el constructor DbContext. Tenga en cuenta que los eventos se pueden adjuntar a dbContext en cualquier momento; no es necesario que esto suceda en el constructor de contexto.

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

Ambos eventos son necesarios porque las nuevas entidades activan eventos Tracked cuando son rastreadas por primera vez. StateChanged los eventos solo se disparan para las entidades que cambian de estado mientras ya están siendo rastreadas.

La muestra para este ejemplo contiene una aplicación de consola sencilla que realiza cambios en la base de datos 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();
}

La salida de este código muestra los cambios de estado que se producen y las marcas de tiempo que se aplican:

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