Události .NET v EF Core

Tip

Ukázku událostí si můžete stáhnout z GitHubu.

Entity Framework Core (EF Core) zveřejňuje události .NET, které budou fungovat jako zpětná volání, když v kódu EF Core dojde k určitým věcem. Události jsou jednodušší než průsečíky a umožňují flexibilnější registraci. Jsou ale jenom synchronní, takže nemohou provádět neblokující asynchronní I/O.

Události se registrují na DbContext instanci. Pomocí diagnostického naslouchacího procesu můžete získat stejné informace, ale pro všechny instance DbContextu v rámci tohoto procesu.

Události vyvolané EF Core

EF Core vyvolává následující události:

Událost Při vyvolání
DbContext.SavingChanges Na začátku SaveChanges nebo SaveChangesAsync
DbContext.SavedChanges Na konci úspěšného SaveChanges nebo SaveChangesAsync
DbContext.SaveChangesFailed Na konci neúspěšného nebo neúspěšného SaveChangesSaveChangesAsync
ChangeTracker.Tracked Když je entita sledována kontextem
ChangeTracker.StateChanged Při změně stavu sledované entity

Příklad: Změny stavu časového razítka

Každá entita, kterou sleduje DbContext, má .EntityState Například stav označuje, Added že entita bude vložena do databáze.

V tomto příkladu se Tracked pomocí událostí StateChanged zjistí, kdy entita změní stav. Potom entitu označí aktuálním časem označujícím, kdy k této změně došlo. Výsledkem jsou časové razítka označující, kdy byla entita vložena, odstraněna nebo naposledy aktualizována.

Typy entit v tomto příkladu implementují rozhraní, které definuje vlastnosti časového razítka:

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

Metoda v DbContext aplikace pak může nastavit časové razítka pro libovolnou entitu, která implementuje toto rozhraní:

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

Tato metoda má odpovídající podpis, který se má použít jako obslužná rutina události jak pro události, tak TrackedStateChanged pro události. Obslužná rutina je registrována pro obě události v konstruktoru DbContext. Mějte na paměti, že události lze kdykoli připojit k DbContext; to není nutné, aby k tomu došlo v kontextovém konstruktoru.

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

Obě události jsou potřeba, protože nové entity se aktivují Tracked při prvním sledování událostí. StateChanged Události se aktivují pouze u entit, které mění stav, zatímco jsou již sledovány.

Ukázka pro tento příklad obsahuje jednoduchou konzolovou aplikaci, která provádí změny v databázi blogování:

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

Výstup z tohoto kódu ukazuje změny stavu a použité časové razítko:

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