.NET-Ereignisse in EF Core

Tipp

Sie können das Ereignisbeispiel in diesem Artikel von GitHub herunterladen.

Entity Framework Core (EF Core) stellt .NET-Ereignisse bereit, die als Rückrufe fungieren, wenn bestimmte Dinge im EF Core-Code auftreten. Ereignisse sind einfacher als Interceptors und ermöglichen eine flexiblere Registrierung. Sie sind jedoch nur synchron und können daher keine nicht blockierenden asynchronen E/A-Vorgänge durchführen.

Ereignisse werden pro DbContext Instanz registriert. Verwenden Sie einen Diagnoselistener, um dieselben Informationen abzurufen, jedoch für alle DbContext-Instanzen im Prozess.

Ereignisse, die von EF Core ausgelöst wurden

Die folgenden Ereignisse werden von EF Core ausgelöst:

Ereignis Beim Auslösen
DbContext.SavingChanges Zu Beginn von SaveChanges oder SaveChangesAsync
DbContext.SavedChanges Am Ende einer erfolgreichen SaveChanges oder SaveChangesAsync
DbContext.SaveChangesFailed Am Ende eines fehlgeschlagenen SaveChanges oder SaveChangesAsync
ChangeTracker.Tracked Wenn eine Entität vom Kontext nachverfolgt wird
ChangeTracker.StateChanged Wenn eine nachverfolgte Entität ihren Status ändert

Beispiel: Änderungen des Zeitstempelzustands

Jede Entität, die von einem DbContext nachverfolgt wird, weist eine EntityState auf. Beispielsweise gibt der Added-Zustand an, dass die Entität in die Datenbank eingefügt wird.

In diesem Beispiel werden die Ereignisse Tracked und StateChanged verwendet, um zu erkennen, wann sich ein Entitätsstatus ändert. Es versieht die Entität dann mit der aktuellen Zeit, die angibt, wann diese Änderung stattgefunden hat. Dies führt zu Zeitstempeln, die angeben, wann die Entität eingefügt, gelöscht und/oder zuletzt aktualisiert wurde.

Die Entitätstypen in diesem Beispiel implementieren eine Schnittstelle, die die Zeitstempeleigenschaften definiert:

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

Eine Methode im DbContext der Anwendung kann dann Zeitstempel für jede Entität setzen, die diese Schnittstelle implementiert:

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

Diese Methode verfügt über die entsprechende Signatur, die als Ereignishandler für die Ereignisse Tracked und StateChanged verwendet werden soll. Der Handler wird für beide Ereignisse im DbContext-Konstruktor registriert. Beachten Sie, dass Ereignisse jederzeit an einen DbContext angehängt werden können; es ist nicht erforderlich, dass dies im Kontextkonstruktor geschieht.

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

Beide Ereignisse werden benötigt, da neue Entitäten Tracked-Ereignisse auslösen, wenn sie zum ersten Mal nachverfolgt werden. StateChanged Ereignisse werden nur für Entitäten ausgelöst, die den Zustand ändern, während sie bereits nachverfolgt werden.

Die Stichprobe für dieses Beispiel enthält eine einfache Konsolenanwendung, die Änderungen an der Blogging-Datenbank vornimmt:

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

In der Ausgabe dieses Codes werden die Zustandsänderungen und die angewendeten Zeitstempel angezeigt:

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