Événements .NET dans EF Core

Conseil

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

Entity Framework Core (EF Core) expose les Événements .NET pour agir en tant que rappels quand certains événements se produisent dans le code EF Core. Les événements sont plus simples que des 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.

Des événements sont inscrits par instance DbContext. Utilisez un écouteur de diagnostic pour obtenir les mêmes informations, mais pour l’ensemble des 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 Lors du déclenchement
DbContext.SavingChanges Au début de SaveChanges ou SaveChangesAsync
DbContext.SavedChanges À la fin de SaveChanges ou SaveChangesAsync réussi
DbContext.SaveChangesFailed À la fin de SaveChanges ou SaveChangesAsync en échec
ChangeTracker.Tracked Lorsqu’une entité est suivi par le contexte
ChangeTracker.StateChanged Lorsqu’une entité suivie modifie son état

Example : des changements d’état Timestamp

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

Cet exemple utilise les événements Tracked et StateChanged pour détecter le changement d’état d’une entité. Il applique ensuite l’heure actuelle à l’entité indiquant le moment où ce changement s’est produit. Il est résulte des timestamps qui indiquent le moment où l’entité a été insérée, supprimée et/ou mise à jour pour la dernière fois.

Les types d’entité dans cet exemple implémentent une interface qui définit les propriétés du timestamp :

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

Une méthode sur le DbContext de l’application peut ensuite définir des timestamps pour toute entité implémentant 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 dispose de la signature appropriée pour utiliser un 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 vous pouvez joindre des événements à 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 requis, car de nouvelles entités déclenchent des événements Tracked quand elles sont suivies en premier. Des événements StateChanged peuvent uniquement être déclenchés pour des entités qui changent d’état alors qu’elles sont déjà en cours de suivi.

L’exemple contient une application de console simple qui apporte des changements à la base de données de blogs :

using (var context = new BlogsContext())
{
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();

    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!" } }
        });

    context.SaveChanges();
}

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

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

    context.SaveChanges();
}

La sortie à partir de ce code montre les changements d’état qui se produisent et les timestamps en cours d’application :

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