エンティティの状態の操作Working with entity states

このトピックでは、コンテキストにエンティティを追加してアタッチする方法と、それらを SaveChanges 中に Entity Framework 処理する方法について説明します。This topic will cover how to add and attach entities to a context and how Entity Framework processes these during SaveChanges. Entity Framework は、コンテキストへの接続中にエンティティの状態を追跡しますが、切断されたシナリオや N 層のシナリオでは、エンティティの状態を EF に知らせることができます。Entity Framework takes care of tracking the state of entities while they are connected to a context, but in disconnected or N-Tier scenarios you can let EF know what state your entities should be in. このトピックで紹介するテクニックは、Code First および EF Designer で作成されたモデルに等しく使用できます。The techniques shown in this topic apply equally to models created with Code First and the EF Designer.

エンティティの状態と SaveChangesEntity states and SaveChanges

エンティティは、EntityState 列挙で定義されている5つの状態のいずれかになります。An entity can be in one of five states as defined by the EntityState enumeration. これらの状態を次に示します。These states are:

  • 追加: エンティティはコンテキストによって追跡されていますが、データベースにまだ存在していませんAdded: the entity is being tracked by the context but does not yet exist in the database
  • Unchanged: エンティティがコンテキストによって追跡され、データベースに存在し、そのプロパティ値がデータベース内の値から変更されていません。Unchanged: the entity is being tracked by the context and exists in the database, and its property values have not changed from the values in the database
  • Modified: エンティティがコンテキストによって追跡され、データベースに存在し、そのプロパティ値の一部またはすべてが変更されています。Modified: the entity is being tracked by the context and exists in the database, and some or all of its property values have been modified
  • Deleted: エンティティがコンテキストによって追跡されていて、データベースに存在するが、次回 SaveChanges が呼び出されたときにデータベースから削除対象としてマークされているDeleted: the entity is being tracked by the context and exists in the database, but has been marked for deletion from the database the next time SaveChanges is called
  • デタッチ済み: エンティティがコンテキストによって追跡されていませんDetached: the entity is not being tracked by the context

SaveChanges は、さまざまな状態のエンティティに対してさまざまな処理を行います。SaveChanges does different things for entities in different states:

  • 変更されていないエンティティは SaveChanges によって操作されません。Unchanged entities are not touched by SaveChanges. 更新プログラムは、変更されていない状態のエンティティのデータベースには送信されません。Updates are not sent to the database for entities in the Unchanged state.
  • 追加されたエンティティはデータベースに挿入され、SaveChanges が返されるときには変更されません。Added entities are inserted into the database and then become Unchanged when SaveChanges returns.
  • 変更されたエンティティはデータベースで更新され、SaveChanges が戻るときに変更されないようになります。Modified entities are updated in the database and then become Unchanged when SaveChanges returns.
  • 削除されたエンティティはデータベースから削除され、その後コンテキストからデタッチされます。Deleted entities are deleted from the database and are then detached from the context.

次の例は、エンティティまたはエンティティグラフの状態を変更できる方法を示しています。The following examples show ways in which the state of an entity or an entity graph can be changed.

コンテキストへの新しいエンティティの追加Adding a new entity to the context

DbSet で Add メソッドを呼び出すことによって、新しいエンティティをコンテキストに追加できます。A new entity can be added to the context by calling the Add method on DbSet. これにより、エンティティは追加された状態になります。つまり、次に SaveChanges が呼び出されたときにデータベースに挿入されます。This puts the entity into the Added state, meaning that it will be inserted into the database the next time that SaveChanges is called. 次に例を示します。For example:

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

コンテキストに新しいエンティティを追加するもう1つの方法は、その状態を "追加" に変更することです。Another way to add a new entity to the context is to change its state to Added. 次に例を示します。For example:

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

最後に、既に追跡されている別のエンティティにフックすることによって、新しいエンティティをコンテキストに追加できます。Finally, you can add a new entity to the context by hooking it up to another entity that is already being tracked. これは、別のエンティティのコレクションナビゲーションプロパティに新しいエンティティを追加するか、新しいエンティティを指す別のエンティティの参照ナビゲーションプロパティを設定することによって行うことができます。This could be by adding the new entity to the collection navigation property of another entity or by setting a reference navigation property of another entity to point to the new entity. 次に例を示します。For example:

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 が呼び出されたときにデータベースに挿入されます。Note that for all of these examples if the entity being added has references to other entities that are not yet tracked then these new entities will also be added to the context and will be inserted into the database the next time that SaveChanges is called.

既存のエンティティをコンテキストにアタッチするAttaching an existing entity to the context

データベースに既に存在しているが、現在コンテキストによって追跡されていないエンティティがある場合は、DbSet で Attach メソッドを使用してエンティティを追跡するようにコンテキストに指示できます。If you have an entity that you know already exists in the database but which is not currently being tracked by the context then you can tell the context to track the entity using the Attach method on DbSet. エンティティは、コンテキスト内の変更されていない状態になります。The entity will be in the Unchanged state in the context. 次に例を示します。For example:

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 を呼び出すと、データベースは変更されないことに注意してください。Note that no changes will be made to the database if SaveChanges is called without doing any other manipulation of the attached entity. これは、エンティティが変更されていない状態であるためです。This is because the entity is in the Unchanged state.

既存のエンティティをコンテキストにアタッチするもう1つの方法は、その状態を変更なしに変更することです。Another way to attach an existing entity to the context is to change its state to Unchanged. 次に例を示します。For example:

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

これらの両方の例で、アタッチされているエンティティがまだ追跡されていない他のエンティティを参照している場合は、これらの新しいエンティティも変更されていない状態でコンテキストにアタッチされます。Note that for both of these examples if the entity being attached has references to other entities that are not yet tracked then these new entities will also attached to the context in the Unchanged state.

既存の変更されたエンティティをコンテキストにアタッチするAttaching an existing but modified entity to the context

データベースに既に存在し、変更が加えられていることがわかっているエンティティがある場合は、エンティティをアタッチするようにコンテキストに指示し、その状態を Modified に設定できます。If you have an entity that you know already exists in the database but to which changes may have been made then you can tell the context to attach the entity and set its state to Modified. 次に例を示します。For example:

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 が呼び出されたときにすべてのプロパティ値がデータベースに送信されます。When you change the state to Modified all the properties of the entity will be marked as modified and all the property values will be sent to the database when SaveChanges is called.

アタッチされているエンティティにまだ追跡されていない他のエンティティへの参照が含まれている場合、これらの新しいエンティティは、変更されていない状態でコンテキストにアタッチされるため、自動的に変更されることはありません。Note that if the entity being attached has references to other entities that are not yet tracked, then these new entities will attached to the context in the Unchanged state—they will not automatically be made Modified. 複数のエンティティを変更済みとしてマークする必要がある場合は、これらの各エンティティの状態を個別に設定する必要があります。If you have multiple entities that need to be marked Modified you should set the state for each of these entities individually.

追跡対象エンティティの状態の変更Changing the state of a tracked entity

エントリの状態プロパティを設定することによって、既に追跡されているエンティティの状態を変更できます。You can change the state of an entity that is already being tracked by setting the State property on its entry. 次に例を示します。For example:

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

既に追跡されているエンティティに対して追加またはアタッチを呼び出すと、エンティティの状態を変更することもできます。Note that calling Add or Attach for an entity that is already tracked can also be used to change the entity state. たとえば、現在追加されているエンティティの Attach を呼び出すと、その状態が Unchanged に変わります。For example, calling Attach for an entity that is currently in the Added state will change its state to Unchanged.

パターンの挿入または更新Insert or update pattern

アプリケーションによっては、主キーの値に応じて、エンティティを新規として追加するか (データベースの挿入を結果として)、エンティティを既存のものとしてアタッチし、変更済みとしてマークする (データベースの更新を作成する) ことがあります。A common pattern for some applications is to either Add an entity as new (resulting in a database insert) or Attach an entity as existing and mark it as modified (resulting in a database update) depending on the value of the primary key. たとえば、データベースによって生成された整数の主キーを使用する場合は、エンティティを new として、0以外のキーを持つエンティティを既存のエンティティとして扱うのが一般的です。For example, when using database generated integer primary keys it is common to treat an entity with a zero key as new and an entity with a non-zero key as existing. このパターンは、主キーの値のチェックに基づいてエンティティの状態を設定することによって実現できます。This pattern can be achieved by setting the entity state based on a check of the primary key value. 次に例を示します。For example:

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

        context.SaveChanges();
    }
}

状態を "変更済み" に変更すると、エンティティのすべてのプロパティが変更済みとしてマークされ、SaveChanges が呼び出されたときにすべてのプロパティ値がデータベースに送信されることに注意してください。Note that when you change the state to Modified all the properties of the entity will be marked as modified and all the property values will be sent to the database when SaveChanges is called.