Share via


EF Core의 .NET 이벤트

EF Core(Entity Framework Core)는 EF Core 코드에서 특정 작업을 수행하는 경우 콜백을 수행할 .NET 이벤트를 노출합니다. 이벤트는 인터셉터보다 간단하며 더 유연하게 등록할 수 있습니다. 그러나 동기화 전용이므로 비차단 비동기 I/O를 수행할 수 없습니다.

이벤트는 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 상태는 엔터티가 데이터베이스에 삽입됨을 나타냅니다.

이 예제에서는 TrackedStateChanged 이벤트를 사용하여 엔터티가 상태를 변경하는 시기를 감지합니다. 그런 다음, 이 변경이 발생한 시기를 나타내는 현재 시간으로 엔터티에 스탬프를 찍습니다. 이렇게 하면 엔터티가 삽입, 삭제 및/또는 마지막으로 업데이트된 시기를 나타내는 타임스탬프가 생성됩니다.

이 예제의 엔터티 형식은 타임스탬프 속성을 정의하는 인터페이스를 구현합니다.

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

이 메서드에는 TrackedStateChanged 이벤트 모두에 대한 이벤트 처리기로 사용할 적절한 서명이 있습니다. 처리기는 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