Partager via


Événements .NET dans EF Core

Conseil / Astuce

Vous pouvez télécharger l’exemple d’événements à partir de GitHub.

Entity Framework Core (EF Core) expose les événements .NET pour agir en tant que rappels lorsque certaines choses se produisent dans le code EF Core. Les événements sont plus simples que les intercepteurs et permettent une inscription plus flexible. Toutefois, ils sont synchronisés uniquement et ne peuvent donc pas effectuer d’E/S asynchrones non bloquantes.

Les événements sont inscrits par DbContext instance. Utilisez un écouteur de diagnostic pour obtenir les mêmes informations, mais pour toutes les instances DbContext dans le processus.

Événements déclenchés par EF Core

Les événements suivants sont déclenchés par EF Core :

Événement Lorsqu'il est soulevé
DbContext.SavingChanges Au début de SaveChanges ou SaveChangesAsync
DbContext.SavedChanges À la fin d’un événement réussi SaveChanges ou SaveChangesAsync
DbContext.SaveChangesFailed À la fin d’un échec SaveChanges ou SaveChangesAsync
ChangeTracker.Tracked Lorsqu’une entité est suivie par le contexte
ChangeTracker.StateChanged Lorsqu’une entité suivie change d’état

Exemple : Modifications de l’état d’horodatage

Chaque entité suivie par un DbContext a un EntityState. Par exemple, l’état Added indique que l’entité sera insérée dans la base de données.

Cet exemple utilise les événements Tracked et StateChanged pour détecter quand une entité change d’état. Il marque ensuite l’entité avec l’heure actuelle indiquant quand cette modification s’est produite. Cela entraîne des horodatages indiquant quand l’entité a été insérée, supprimée et/ou mise à jour la dernière fois.

Les types d’entités de cet exemple implémentent une interface qui définit les propriétés d’horodatage :

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

Une méthode sur dbContext de l’application peut ensuite définir des horodatages pour n’importe quelle entité qui implémente cette 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;
        }
    }
}

Cette méthode a la signature appropriée pour être utilisée comme gestionnaire d’événements pour les événements Tracked et StateChanged. Le gestionnaire est inscrit pour les deux événements dans le constructeur DbContext. Notez que les événements peuvent être attachés à un DbContext à tout moment ; il n’est pas nécessaire que cela se produise dans le constructeur de contexte.

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

Les deux événements sont nécessaires, car de nouvelles entités déclenchent Tracked des événements lorsqu’elles sont suivies pour la première fois. StateChanged les événements sont déclenchés uniquement pour les entités qui changent d’état pendant qu’elles sont déjà suivies.

L’exemple de cet exemple contient une application console simple qui apporte des modifications à la base de données 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 sortie de ce code montre les modifications d’état qui se produisent et les horodatages appliqués :

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