次の方法で共有


エンティティの状態を扱う

このトピックでは、エンティティを追加してコンテキストにアタッチする方法と、Entity Framework が SaveChanges 中にエンティティを処理する方法について説明します。 Entity Framework では、エンティティがコンテキストに接続されている間にエンティティの状態を追跡しますが、切断または N 層のシナリオでは、エンティティの状態を EF に知らせることができます。 このトピックで示す手法は、Code First と EF Designer で作成されたモデルにも同様に適用されます。

エンティティの状態と SaveChanges

エンティティは、EntityState 列挙型で定義されている 5 つの状態のいずれかになります。 これらの状態は次のとおりです。

  • 追加: エンティティはコンテキストによって追跡されていますが、データベースにはまだ存在しません
  • 変更なし: エンティティはコンテキストによって追跡され、データベースに存在し、そのプロパティ値がデータベース内の値から変更されていません
  • 変更: エンティティはコンテキストによって追跡され、データベースに存在し、そのプロパティ値の一部またはすべてが変更されました
  • 削除済み: エンティティはコンテキストによって追跡され、データベース内に存在しますが、次に SaveChanges が呼び出されるときにデータベースから削除されるようにマークされています
  • デタッチ済み: エンティティがコンテキストによって追跡されていません

SaveChanges は、さまざまな状態のエンティティに対してさまざまな処理を行います。

  • 変更されていないエンティティは、SaveChanges によって処理されません。 変更されていない状態のエンティティについては、更新はデータベースに送信されません。
  • 追加されたエンティティはデータベースに挿入され、SaveChanges が返されると変更されません。
  • 変更されたエンティティはデータベース内で更新され、SaveChanges が返されると変更されません。
  • 削除されたエンティティはデータベースから削除され、コンテキストからデタッチされます。

次の例は、エンティティまたはエンティティ グラフの状態を変更する方法を示しています。

コンテキストへの新しいエンティティの追加

DbSet で Add メソッドを呼び出すことで、新しいエンティティをコンテキストに追加できます。 これにより、エンティティは追加状態になります。つまり、次に SaveChanges が呼び出されるときにデータベースに挿入されます。 例えば次が挙げられます。

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

コンテキストに新しいエンティティを追加するもう 1 つの方法は、その状態を [追加] に変更することです。 例えば次が挙げられます。

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

最後に、コンテキストに新しいエンティティを追加するには、既に追跡されている別のエンティティにフックします。 これは、新しいエンティティを別のエンティティのコレクション ナビゲーション プロパティに追加するか、新しいエンティティを指す別のエンティティの参照ナビゲーション プロパティを設定することで行うことができます。 例えば次が挙げられます。

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

追加されるエンティティがまだ追跡されていない他のエンティティへの参照がある場合、これらのすべての例では、これらの新しいエンティティもコンテキストに追加され、次に SaveChanges が呼び出されるときにデータベースに挿入されることに注意してください。

既存のエンティティをコンテキストにアタッチする

データベースに既に存在することがわかっているが、コンテキストによって現在追跡されていないエンティティがある場合は、DbSet の Attach メソッドを使用してエンティティを追跡するようにコンテキストに指示できます。 エンティティは、コンテキスト内で変更されていない状態になります。 例えば次が挙げられます。

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

アタッチされたエンティティの他の操作を行わずに SaveChanges が呼び出された場合、データベースに対する変更は行われません。 これは、エンティティが変更されていない状態であるためです。

既存のエンティティをコンテキストにアタッチするもう 1 つの方法は、その状態を「未変更」に設定することです。 例えば次が挙げられます。

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

これらの両方の例で、アタッチされているエンティティがまだ追跡されていない他のエンティティへの参照がある場合、これらの新しいエンティティも変更されていない状態のコンテキストにアタッチされることに注意してください。

既存のエンティティを変更してコンテキストにアタッチする

データベースに既に存在することがわかっているエンティティがあり、変更が加えられた可能性があるエンティティがある場合は、エンティティをアタッチし、その状態を Modified に設定するようにコンテキストに指示できます。 例えば次が挙げられます。

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

状態を [変更済み] に変更すると、エンティティのすべてのプロパティが変更済みとしてマークされ、SaveChanges が呼び出されたときにすべてのプロパティ値がデータベースに送信されます。

アタッチされているエンティティに、まだ追跡されていない他のエンティティへの参照がある場合、これらの新しいエンティティは変更されていない状態のコンテキストにアタッチされ、自動的には変更されないことに注意してください。 [変更済み] とマークする必要があるエンティティが複数ある場合は、これらの各エンティティの状態を個別に設定する必要があります。

追跡対象エンティティの状態の変更

エントリで State プロパティを設定することで、既に追跡されているエンティティの状態を変更できます。 例えば次が挙げられます。

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

既に追跡されているエンティティに対して Add または Attach を呼び出して、エンティティの状態を変更することもできます。 たとえば、現在 [追加済み] 状態にあるエンティティに対して Attach を呼び出すと、その状態が [変更なし] に変わります。

パターンの挿入または更新

一部のアプリケーションの一般的なパターンは、プライマリ キーの値に応じて、エンティティを新しいエンティティとして追加する (データベース挿入が発生する) か、エンティティを既存のエンティティとしてアタッチし、変更済みとしてマークする (データベースの更新が行われる) 方法です。 たとえば、データベースで生成された整数主キーを使用する場合、ゼロ キーを持つエンティティを新規として扱い、0 以外のキーを持つエンティティを既存のエンティティとして扱うのが一般的です。 このパターンは、主キー値のチェックに基づいてエンティティの状態を設定することで実現できます。 例えば次が挙げられます。

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

状態を [変更済み] に変更すると、エンティティのすべてのプロパティが変更済みとしてマークされ、SaveChanges が呼び出されたときにすべてのプロパティ値がデータベースに送信されることに注意してください。