Dela via


.NET-händelser i EF Core

Tips/Råd

Du kan ladda ned händelseexemplet från GitHub.

Entity Framework Core (EF Core) exponerar .NET-händelser för att fungera som återanrop när vissa saker händer i EF Core-koden. Händelser är enklare än interceptorer och tillåter ett mer flexibelt sätt att registrera dem. De är dock endast synkroniserade och kan därför inte utföra icke-blockerande asynkrona I/O.

Händelser registreras per DbContext enhet. Använd en diagnostiklyssnare för att få samma information men för alla DbContext-instanser i processen.

Händelser som genereras av EF Core

Följande händelser genereras av EF Core:

Evenemang När det höjs
DbContext.SavingChanges I början av SaveChanges eller SaveChangesAsync
DbContext.SavedChanges I slutet av en lyckad SaveChanges eller SaveChangesAsync
DbContext.SaveChangesFailed I slutet av en misslyckad SaveChanges eller SaveChangesAsync
ChangeTracker.Tracked När en entitet spåras av kontexten
ChangeTracker.StateChanged När en spårad entitet ändrar sitt tillstånd

Exempel: Ändringar i tidsstämpeltillstånd

Varje entitet som spåras av en DbContext har en EntityState. Till exempel indikerar tillståndet Added att entiteten kommer att infogas i databasen.

I det här exemplet används Tracked- och StateChanged-händelserna för att identifiera när en entitet ändrar tillstånd. Den stämplar sedan entiteten med den aktuella tiden som anger när den här ändringen inträffade. Detta resulterar i tidsstämplar som anger när entiteten infogades, togs bort och/eller senast uppdaterades.

Entitetstyperna i det här exemplet implementerar ett gränssnitt som definierar tidsstämpelegenskaperna:

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

En metod i programmets DbContext kan sedan ange tidsstämplar för alla entiteter som implementerar det här gränssnittet:

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

Den här metoden har lämplig signatur att använda som händelsehanterare för både Tracked händelserna och StateChanged . Hanteraren är registrerad för båda händelserna i DbContext-konstruktorn. Observera att händelser kan kopplas till en DbContext när som helst. det krävs inte att detta sker i kontextkonstruktorn.

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

Båda händelserna behövs eftersom nya entiteter utlöser Tracked händelser när de först spåras. StateChanged händelser utlöses endast för entiteter som ändrar tillstånd medan de redan är under spårning.

Exemplet för det här exemplet innehåller ett enkelt konsolprogram som gör ändringar i bloggdatabasen:

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

Utdata från den här koden visar tillståndsändringar som sker och de tidsstämplar som tillämpas:

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