追跡と追跡なしのクエリTracking vs. No-Tracking Queries

Entity Framework Core が、変更追跡のエンティティ インスタンスに関する情報を保持するかどうかは、追跡の動作によって制御されます。Tracking behavior controls whether or not Entity Framework Core will keep information about an entity instance in its change tracker. エンティティが追跡されている場合、エンティティ内で検出された変更はすべて、SaveChanges() の間にデータベースに対して永続化されます。If an entity is tracked, any changes detected in the entity will be persisted to the database during SaveChanges(). また、Entity Framework Core は、追跡クエリから取得されたエンティティと、DbContext インスタンスに以前に読み込まれたエンティティ間のナビゲーション プロパティを修正します。Entity Framework Core will also fix-up navigation properties between entities that are obtained from a tracking query and entities that were previously loaded into the DbContext instance.

ヒント

この記事のサンプルは GitHub で確認できます。You can view this article's sample on GitHub.

追跡クエリTracking queries

既定では、エンティティ型を返すクエリは、追跡を行います。By default, queries that return entity types are tracking. つまり、それらのエンティティ インスタンスに変更を加えて、SaveChanges() によって永続化された変更を保持できます。This means you can make changes to those entity instances and have those changes persisted by SaveChanges().

次の例では、ブログ評価に対する変更が検出され、SaveChanges() の間にデータベースに対して永続化されています。In the following example, the change to the blogs rating will be detected and persisted to the database during SaveChanges().

using (var context = new BloggingContext())
{
    var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
    blog.Rating = 5;
    context.SaveChanges();
}

追跡なしのクエリNo-tracking queries

追跡なしのクエリは、読み取り専用のシナリオで結果が使用される場合に役立ちます。No tracking queries are useful when the results are used in a read-only scenario. 変更追跡の情報を設定する必要がないので、これらのクエリは、より迅速に実行されます。They are quicker to execute because there is no need to setup change tracking information.

次のように、追跡なしになるように個々のクエリをスワップできます。You can swap an individual query to be no-tracking:

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .AsNoTracking()
        .ToList();
}

また、コンテキスト インスタンスのレベルで、既定の追跡動作を変更できます。You can also change the default tracking behavior at the context instance level:

using (var context = new BloggingContext())
{
    context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

    var blogs = context.Blogs.ToList();
}

注意

追跡なしのクエリでは、実行しているクエリ内で ID の解決を行います。No tracking queries still perform identity resolution within the excuting query. 結果セットに同じエンティティが複数回含まれている場合、結果セット内での各同時実行に対して、エンティティ クラスの同じインスタンスが返されます。If the result set contains the same entity multiple times, the same instance of the entity class will be returned for each occurrence in the result set. ただし、既に返されたエンティティの追跡を継続するために、弱参照が使用されます。However, weak references are used to keep track of entities that have already been returned. 同じ ID の前の結果が範囲外になっている場合は、ガベージ コレクションが実行され、新しいエンティティ インスタンスを取得できます。If a previous result with the same identity goes out of scope, and garbage collection runs, you may get a new entity instance. 詳細については、「クエリのしくみ」を参照してください。For more information, see How Query Works.

追跡と予測Tracking and projections

クエリの結果の型がエンティティ型ではない場合でも、結果にエンティティ型が含まれていれば、既定で引き続き追跡されます。Even if the result type of the query isn't an entity type, if the result contains entity types they will still be tracked by default. 次のクエリでは、匿名の型が返され、結果セット内で Blog のインスタンスが追跡されます。In the following query, which returns an anonymous type, the instances of Blog in the result set will be tracked.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Select(b =>
            new
            {
                Blog = b,
                Posts = b.Posts.Count()
            });
}

結果セットにエンティティ型が含まれていない場合、追跡なしのクエリが実行されます。If the result set does not contain any entity types, then no tracking is performed. 次のクエリでは、エンティティからいくつかの値を持つ (ただし、実際のエンティティ型のインスタンスはない) 匿名の型が返され 、追跡は行われていません。In the following query, which returns an anonymous type with some of the values from the entity (but no instances of the actual entity type), there is no tracking performed.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Select(b =>
            new
            {
                Id = b.BlogId,
                Url = b.Url
            });
}