次の方法で共有


EF Core の .NET イベント

ヒント

イベント サンプルは GitHub からダウンロードできます。

Entity Framework Core (EF 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 状態は、エンティティがデータベースに挿入されることを示します。

この例では、 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())
{
    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();
}

このコードからの出力は、発生している状態の変化と、適用されているタイムスタンプを示しています。

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