Udostępnij przez


Zdarzenia platformy .NET na platformie EF Core

Wskazówka

Przykładowe zdarzenia można pobrać z usługi GitHub.

Program Entity Framework Core (EF Core) udostępnia zdarzenia .NET, by pełnić rolę wywołań zwrotnych, gdy w kodzie EF Core występują pewne zdarzenia. Zdarzenia są prostsze niż przechwytywacze i pozwalają na bardziej elastyczną rejestrację. Jednak działają one tylko w trybie synchronicznym, a więc nie mogą wykonywać nieblokujących asynchronicznych operacji wejścia/wyjścia.

Zdarzenia są rejestrowane na DbContext wystąpienie. Użyj odbiornika diagnostycznego, aby uzyskać te same informacje, ale dla wszystkich wystąpień DbContext w procesie.

Zdarzenia zgłaszane przez program EF Core

Następujące zdarzenia są wywoływane przez program EF Core:

Zdarzenie Po podniesieniu
DbContext.SavingChanges Na początku SaveChanges lub SaveChangesAsync
DbContext.SavedChanges Na końcu udanego procesu SaveChanges lub SaveChangesAsync
DbContext.SaveChangesFailed Na końcu nieudanej próby SaveChanges lub SaveChangesAsync
ChangeTracker.Tracked Kiedy jednostka jest śledzona przez kontekst
ChangeTracker.StateChanged Gdy śledzona jednostka zmienia swój stan

Przykład: zmiany stanu znacznika czasu

Każda jednostka śledzona przez element DbContext ma element EntityState. Na przykład Added stan wskazuje, że jednostka zostanie wstawiona do bazy danych.

W tym przykładzie użyto zdarzeń Tracked i StateChanged do wykrywania, kiedy jednostka zmienia stan. Następnie oznacza jednostkę bieżącą godziną wskazującą, kiedy ta zmiana się wydarzyła. Spowoduje to znaczniki czasu wskazujące, kiedy jednostka została wstawiona, usunięta i/lub ostatnia aktualizacja.

Typy jednostek w tym przykładzie implementują interfejs definiujący właściwości znacznika czasu:

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

Metoda w obiekcie DbContext aplikacji może następnie ustawić znaczniki czasu dla dowolnej jednostki, która implementuje ten interfejs:

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

Ta metoda ma odpowiedni podpis do użycia jako program obsługi zarówno zdarzeń Tracked i StateChanged. Procedura obsługi jest rejestrowana dla obu zdarzeń w konstruktorze klasy DbContext. Należy pamiętać, że zdarzenia mogą być dołączane do obiektu DbContext w dowolnym momencie; nie jest wymagane, aby miało to miejsce w konstruktorze kontekstu.

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

Oba zdarzenia są potrzebne, ponieważ nowe podmioty uruchamiają Tracked zdarzenia, gdy są po raz pierwszy śledzone. StateChanged Zdarzenia są wyzwalane tylko dla jednostek, które zmieniają stan, gdy są już śledzone.

Przykład dla tego przykładu zawiera prostą aplikację konsolową, która wprowadza zmiany w bazie danych blogów:

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

Dane wyjściowe z tego kodu pokazują zmiany stanu i zastosowane znaczniki czasu:

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