Поделиться через


События .NET в EF Core

Совет

Вы можете скачать пример событий из GitHub.

Entity Framework Core (EF Core) предоставляет события .NET для обратного вызова при выполнении определенных действий в коде EF Core. События проще перехватчиков и позволяют более гибкой регистрации. Однако они только синхронизируются и поэтому не могут выполнять неблокирующие асинхронные операции ввода-вывода.

События регистрируются на экземпляр DbContext . С помощью прослушивателя диагностики можно получить ту же информацию, но для всех экземпляров DbContext в процессе.

События, вызванные EF Core

Следующие события вызываются EF Core:

Событие При возникновении
DbContext.SavingChanges В начале SaveChanges или SaveChangesAsync
DbContext.SavedChanges В конце успешного или успешного SaveChanges выполнения SaveChangesAsync
DbContext.SaveChangesFailed В конце неудачного SaveChanges или SaveChangesAsync
ChangeTracker.Tracked Когда сущность отслеживается контекстом
ChangeTracker.StateChanged При изменении состояния отслеживаемой сущности

Пример. Изменения состояния метки времени

Каждая сущность, отслеживаемая dbContext, имеет .EntityState Например, состояние указывает, Added что сущность будет вставлена в базу данных.

В этом примере используются Tracked события и StateChanged события для обнаружения состояния сущности. Затем он метки сущности с текущим временем, указывающим, когда произошло это изменение. Это приводит к меткам времени, указывающим, когда сущность была вставлена, удалена и /или последнее обновление.

Типы сущностей в этом примере реализуют интерфейс, определяющий свойства метки времени:

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

Затем метод dbContext приложения может задать метки времени для любой сущности, реализующей этот интерфейс:

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

Этот метод имеет соответствующую подпись для использования в качестве обработчика событий как для событий, так Tracked и StateChanged для событий. Обработчик регистрируется для обоих событий в конструкторе DbContext. Обратите внимание, что события можно подключить к DbContext в любое время; Не требуется, чтобы это произошло в конструкторе контекста.

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

Оба события необходимы, так как новые сущности запускаются Tracked при первом отслеживании. StateChanged события запускаются только для сущностей, которые изменяют состояние, пока они уже отслеживаются.

Пример этого примера содержит простое консольное приложение, которое вносит изменения в базу данных блогов:

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

В выходных данных этого кода отображаются изменения состояния и примененные метки времени:

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