切断されているエンティティDisconnected entities

DbContext インスタンスでは、データベースから返されるエンティティを自動的に追跡されます。A DbContext instance will automatically track entities returned from the database. SaveChanges が呼び出され、必要に応じて、データベースが更新されるときに、これらのエンティティに加えられた変更を検出し、されます。Changes made to these entities will then be detected when SaveChanges is called and the database will be updated as needed. 参照してください基本的な保存関連データ詳細についてはします。See Basic Save and Related Data for details.

ただし、場合によってエンティティを照会コンテキストの 1 つのインスタンスを使用して、別のインスタンスを使用して保存します。However, sometimes entities are queried using one context instance and then saved using a different instance. これは、ここでエンティティはクエリを実行、クライアントに送信される、変更、要求でサーバーに送信されるおよび保存し、web アプリケーションなどの「切断済み」のシナリオでよく発生します。This often happens in "disconnected" scenarios such as a web application where the entities are queried, sent to the client, modified, sent back to the server in a request, and then saved. ここでは、2 つ目のコンテキスト エンティティが新しいかどうかを知る (を挿入する) 必要がありますか (更新する必要があります) を既存をインスタンスします。In this case, the second context instance needs to know whether the entities are new (should be inserted) or existing (should be updated).

ヒント

この記事を表示するサンプルGitHub でします。You can view this article's sample on GitHub.

新しいエンティティを識別します。Identifying new entities

クライアントが新しいエンティティを識別します。Client identifies new entities

最も単純なに対処するしている場合、エンティティは、新しいまたは既存かどうかをクライアントがサーバーに通知します。The simplest case to deal with is when the client informs the server whether the entity is new or existing. たとえば、多くの場合、新しいエンティティを挿入する要求は、既存のエンティティを更新する要求を異なるです。For example, often the request to insert a new entity is different from the request to update an existing entity.

このセクションの残りの部分は、ケースをカバーして、その他の何らかの方法で挿入または更新するかどうか判断するために必要です。The remainder of this section covers the cases where it necessary to determine in some other way whether to insert or update.

自動生成キーを持つWith auto-generated keys

自動的に生成されたキーの値は、エンティティを挿入または更新する必要があるかどうかを決定する多くの場合、使用できます。The value of an automatically generated key can often be used to determine whether an entity needs to be inserted or updated. キーが設定されて (つまり、まだ null、0 を返しなどの CLR の既定値)、エンティティが新しいである必要があるあり、挿入する必要があります。If the key has not been set (i.e. it still has the CLR default value of null, zero, etc.), then the entity must be new and needs inserting. その一方で、キーの値が設定されている場合、必要がありますが既に保存されている以前し更新が必要なようになりました。On the other hand, if the key value has been set, then it must have already been previously saved and now needs updating. つまり、キーの値が、し、エンティティの照会、クライアントに送信されるがようになりました戻ると更新します。In other words, if the key has a value, then entity was queried, sent to the client, and has now come back to be updated.

これは、エンティティ型がわかっている場合に設定されていないキーの確認簡単です。It is easy to check for an unset key when the entity type is known:

public static bool IsItNew(Blog blog) 
    => blog.BlogId == 0;

ただし、EF は、これを行うすべてのエンティティ型とキーの種類の機能を組み込むこともあります。However, EF also has a built-in way to do this for any entity type and key type:

public static bool IsItNew(DbContext context, object entity) 
    => !context.Entry(entity).IsKeySet;

ヒント

キーは、場合でも、状態が Added では、エンティティのエンティティが、コンテキストによって追跡されるとすぐに設定されます。Keys are set as soon as entities are tracked by the context, even if the entity is in the Added state. これは、エンティティと TrackGraph API を使用する場合と同様、このような処理を決定のグラフを走査する場合に役立ちます。This helps when traversing a graph of entities and deciding what to do with each, such as when using the TrackGraph API. キーの値は、次に示す方法でのみ使用する必要があります_する前に_エンティティを追跡するために、呼び出しが行われます。The key value should only be used in the way shown here before any call is made to track the entity.

その他のキーを持つWith other keys

キーの値が自動的に生成されたいないときに、新しいエンティティを識別するその他の機構が必要です。Some other mechanism is needed to identity new entities when key values are not generated automatically. この 2 つの一般的な方法があります。There are two general approaches to this:

  • エンティティのクエリQuery for the entity
  • クライアントから、フラグを渡しますPass a flag from the client

クエリを実行するエンティティのだけ検索メソッドを使用します。To query for the entity, just use the Find method:

public static bool IsItNew(BloggingContext context, Blog blog)
    => context.Blogs.Find(blog.BlogId) == null;

クライアントからフラグを渡すため完全なコードを表示するには、このドキュメントの範囲を超えています。It is beyond the scope of this document to show the full code for passing a flag from a client. Web アプリで通常意味をさまざまな操作は、別の要求を行うか、要求にいくつかの状態を渡すこと、コント ローラーに抽出します。In a web app, it usually means making different requests for different actions, or passing some state in the request then extracting it in the controller.

単一のエンティティの保存Saving single entities

場合は認識されているかどうか、insert または update は、必要な追加または更新のいずれかを適切に使用することができます。If it is known whether or not an insert or update is needed, then either Add or Update can be used appropriately:

public static void Insert(DbContext context, object entity)
{
    context.Add(entity);
    context.SaveChanges();
}

public static void Update(DbContext context, object entity)
{
    context.Update(entity);
    context.SaveChanges();
}

ただし、エンティティは、自動生成されたキー値を使用する場合のどちらの場合、Update メソッドを使用できます。However, if the entity uses auto-generated key values, then the Update method can be used for both cases:

public static void InsertOrUpdate(DbContext context, object entity)
{
    context.Update(entity);
    context.SaveChanges();
}

Update メソッドは、通常、更新、挿入ではないエンティティをマークします。The Update method normally marks the entity for update, not insert. ただし、エンティティが、自動生成されたキーとキーの値が設定されていない、エンティティは代わりに自動的に設定されていますし、次のように挿入します。However, if the entity has a auto-generated key, and no key value has been set, then the entity is instead automatically marked for insert.

ヒント

この動作は、EF コア 2.0 で導入されました。This behavior was introduced in EF Core 2.0. 以前のリリースは常に使用するために必要な明示的に追加] または [更新プログラムを選択します。For earlier releases it is always necessary to explicitly choose either Add or Update.

エンティティは、自動生成キーを使用していないかどうかは、アプリケーションは、エンティティを挿入または更新するかどうかを決定する必要があります。 例。If the entity is not using auto-generated keys, then the application must decide whether the entity should be inserted or updated: For example:

public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs.Find(blog.BlogId);
    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
    }

    context.SaveChanges();
}

ここに示す手順は次のとおりです。The steps here are:

  • 検索を返します。 null の場合、データベースに含まれていないこの ID を持つブログおを呼び出すようにし、追加する場合は、挿入のためマークします。If Find returns null, then the database doesn't already contain the blog with this ID, so we call Add mark it for insertion.
  • 検索では、エンティティが返された場合、データベース内に存在する、およびコンテキストは既存のエンティティを追跡してようになりましたIf Find returns an entity, then it exists in the database and the context is now tracking the existing entity
    • SetValues を使用して、クライアントから得られたものには、このエンティティのすべてのプロパティの値を設定します。We then use SetValues to set the values for all properties on this entity to those that came from the client.
    • SetValues 呼び出しでは、必要に応じて更新するエンティティをマークします。The SetValues call will mark the entity to be updated as needed.

ヒント

追跡対象エンティティの別の値を持つプロパティを変更済みとして SetValues にマークされるだけです。SetValues will only mark as modified the properties that have different values to those in the tracked entity. これは、更新が送信されると、実際に変更された列のみが更新されることを意味します。This means that when the update is sent, only those columns that have actually changed will be updated. (と何が変更された場合、更新プログラムは送信されませんまったく)。(And if nothing has changed, then no update will be sent at all.)

グラフの操作Working with graphs

すべての新しいまたはすべて既存のエンティティAll new/all existing entities

グラフの操作の例を挿入またはその関連する投稿のコレクションと共にブログを更新するとします。An example of working with graphs is inserting or updating a blog together with its collection of associated posts. グラフ内のすべてのエンティティを挿入する、またはすべてを更新する、し、プロセスが単一のエンティティの上記と同じです。If all the entities in the graph should be inserted, or all should be updated, then the process is the same as described above for single entities. たとえば、ブログと次のように作成された投稿のグラフ:For example, a graph of blogs and posts created like this:

var blog = new Blog
{
    Url = "http://sample.com",
    Posts = new List<Post>
    {
        new Post {Title = "Post 1"},
        new Post {Title = "Post 2"},
    }
};

次のように挿入することができます。can be inserted like this:

public static void InsertGraph(DbContext context, object rootEntity)
{
    context.Add(rootEntity);
    context.SaveChanges();
}

追加の呼び出しは、ブログや挿入されるすべての投稿にマークされます。The call to Add will mark the blog and all the posts to be inserted.

同様に、グラフ内のすべてのエンティティを更新する必要がある場合、更新プログラムを使用できます。Likewise, if all the entities in a graph need to be updated, then Update can be used:

public static void UpdateGraph(DbContext context, object rootEntity)
{
    context.Update(rootEntity);
    context.SaveChanges();
}

ブログとそのすべての投稿を更新するマークされます。The blog and all its posts will be marked to be updated.

新規および既存のエンティティのミックスMix of new and existing entities

、自動生成キーを持つ更新もう一度使用できます挿入と更新の両方の場合でも、グラフには、さまざまなエンティティを挿入するを必要として更新の必要なものが含まれます。With auto-generated keys, Update can again be used for both inserts and updates, even if the graph contains a mix of entities that require inserting and those that require updating:

public static void InsertOrUpdateGraph(DbContext context, object rootEntity)
{
    context.Update(rootEntity);
    context.SaveChanges();
}

更新プログラムは、グラフ、ブログまたは post の場合に、キーの値が設定されていない、更新プログラムの他のすべてのエンティティがマークされているときに挿入するため、すべてのエンティティとしてマークされます。Update will mark any entity in the graph, blog or post, for insertion if it does not have a key value set, while all other entities are marked for update.

としてする前に、自動生成キーを使用していない場合、クエリといくつかの処理を使用できます。As before, when not using auto-generated keys, a query and some processing can be used:

public static void InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs
        .Include(b => b.Posts)
        .FirstOrDefault(b => b.BlogId == blog.BlogId);

    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
        foreach (var post in blog.Posts)
        {
            var existingPost = existingBlog.Posts
                .FirstOrDefault(p => p.PostId == post.PostId);

            if (existingPost == null)
            {
                existingBlog.Posts.Add(post);
            }
            else
            {
                context.Entry(existingPost).CurrentValues.SetValues(post);
            }
        }
    }

    context.SaveChanges();
}

削除の処理Handling deletes

削除はされてから処理する複雑になることが多くの場合、エンティティがない場合では削除する必要があります。Delete can be tricky to handle since often the absence of an entity means that it should be deleted. これに対処する 1 つの方法では、「回復可能な削除」を使用して、エンティティが実際に削除されるのではなく削除済みとしてマークされているようです。One way to deal with this is to use "soft deletes" such that the entity is marked as deleted rather than actually being deleted. 削除してから、更新プログラムと同じになります。Deletes then becomes the same as updates. 使用して実装できる回復可能な削除フィルターをクエリです。Soft deletes can be implemented in using query filters.

場合は true。 削除は、一般的なパターンでは、クエリ パターンの拡張機能を使用して graph 相違では基本的には、何を実行するには例:For true deletes, a common pattern is to use an extension of the query pattern to perform what is essentially a graph diff. For example:

public static void InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs
        .Include(b => b.Posts)
        .FirstOrDefault(b => b.BlogId == blog.BlogId);

    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
        foreach (var post in blog.Posts)
        {
            var existingPost = existingBlog.Posts
                .FirstOrDefault(p => p.PostId == post.PostId);

            if (existingPost == null)
            {
                existingBlog.Posts.Add(post);
            }
            else
            {
                context.Entry(existingPost).CurrentValues.SetValues(post);
            }
        }

        foreach (var post in existingBlog.Posts)
        {
            if (!blog.Posts.Any(p => p.PostId == post.PostId))
            {
                context.Remove(post);
            }
        }
    }

    context.SaveChanges();
}

TrackGraphTrackGraph

内部的には、追加、アタッチ、および Update を使用して graph トラバーサルを Unchanged にするかどうか、としてマークしておく (挿入) を追加、変更 (更新) を各エンティティに対して行われた判断 (何もしない)、または (削除) を削除します。Internally, Add, Attach, and Update use graph-traversal with a determination made for each entity as to whether it should be marked as Added (to insert), Modified (to update), Unchanged (do nothing), or Deleted (to delete). このメカニズムは TrackGraph API を介して公開されます。This mechanism is exposed via the TrackGraph API. たとえば、クライアントがエンティティのグラフを返信ときにいくつかフラグを設定の処理方法を示す各エンティティを行うことと仮定します。For example, let's assume that when the client sends back a graph of entities it sets some flag on each entity indicating how it should be handled. TrackGraph はこのフラグを処理するために使用できます。TrackGraph can then be used to process this flag:

public static void SaveAnnotatedGraph(DbContext context, object rootEntity)
{
    context.ChangeTracker.TrackGraph(
        rootEntity,
        n =>
        {
            var entity = (EntityBase)n.Entry.Entity;
            n.Entry.State = entity.IsNew
                ? EntityState.Added
                : entity.IsChanged
                    ? EntityState.Modified
                    : entity.IsDeleted
                        ? EntityState.Deleted
                        : EntityState.Unchanged;
        });

    context.SaveChanges();
}

フラグは、わかりやすくするための例では、エンティティの一部としてのみ表示されます。The flags are only shown as part of the entity for simplicity of the example. 通常、フラグは DTO または要求に含まれるその他の状態の一部になります。Typically the flags would be part of a DTO or some other state included in the request.