추적 및 비 추적 쿼리Tracking vs. No-Tracking Queries

추적 동작은 Entity Framework Core가 해당 변경 추적 장치에서 엔터티 인스턴스에 대한 정보를 유지할지 여부를 제어합니다.Tracking behavior controls if 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(). EF Core는 추적 쿼리 결과에 있는 엔터티와 변경 추적 장치에 있는 엔터티 간 탐색 속성도 수정합니다.EF Core will also fix up navigation properties between the entities in a tracking query result and the entities that are in the change tracker.

참고

키 없는 엔터티 형식은 추적되지 않습니다.Keyless entity types are never tracked. 이 문서에서 언급되는 모든 엔터티 형식은 키가 정의된 엔터티 형식을 가리킵니다.Wherever this article mentions entity types, it refers to entity types which have a key defined.

GitHub에서 이 문서의 샘플을 볼 수 있습니다.You can view this article's sample on GitHub.

추적 쿼리Tracking queries

기본적으로 엔터티 형식을 반환하는 쿼리는 추적됩니다.By default, queries that return entity types are tracking. 즉, 해당 엔터티 인스턴스를 변경하고 SaveChanges()에서 해당 변경 내용을 유지하도록 할 수 있습니다.Which 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().

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're quicker to execute because there's no need to set up the change tracking information. 데이터베이스에서 검색된 엔터티를 업데이트할 필요가 없는 경우 비 추적 쿼리를 사용해야 합니다.If you don't need to update the entities retrieved from the database, then a no-tracking query should be used. 개별 쿼리를 비 추적 쿼리로 전환할 수 있습니다.You can swap an individual query to be no-tracking.

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

컨텍스트 인스턴스 수준에서 기본 추적 동작을 변경할 수도 있습니다.You can also change the default tracking behavior at the context instance level:

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = context.Blogs.ToList();

ID 확인Identity resolution

추적 쿼리는 변경 추적 장치를 사용하므로 EF Core는 추적 쿼리에서 ID 확인을 수행합니다.Since a tracking query uses the change tracker, EF Core will do identity resolution in a tracking query. 엔터티를 구체화할 때 엔터티 인스턴스가 이미 추적 중인 경우 EF Core는 변경 추적기에서 동일한 엔터티 인스턴스를 반환합니다.When materializing an entity, EF Core will return the same entity instance from the change tracker if it's already being tracked. 결과에 동일한 엔터티가 여러 번 포함되는 경우 발생할 때마다 동일한 인스턴스를 다시 가져옵니다.If the result contains same entity multiple times, you get back same instance for each occurrence. 비 추적 쿼리는 변경 추적 장치를 사용하지 않으며 ID 확인을 수행하지 않습니다.No-tracking queries don't use the change tracker and don't do identity resolution. 따라서 동일한 엔터티가 결과에 여러 번 포함되는 경우에도 엔터티의 새 인스턴스를 다시 가져옵니다.So you get back new instance of entity even when the same entity is contained in the result multiple times. 이 동작은 EF Core 3.0 이전 버전과는 다릅니다. 이전 버전을 참조하세요.This behavior was different in versions before EF Core 3.0, see previous versions.

추적 및 사용자 지정 프로젝션Tracking and custom projections

쿼리의 결과 형식이 엔터티 형식이 아닌 경우에도 EF Core는 결과에 포함된 엔터티 형식을 기본적으로 추적합니다.Even if the result type of the query isn't an entity type, EF Core will still track entity types contained in the result by default. 무명 형식을 반환하는 다음 쿼리에서는 결과 집합에 있는 Blog의 인스턴스가 추적됩니다.In the following query, which returns an anonymous type, the instances of Blog in the result set will be tracked.

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

결과 집합에 LINQ 컴퍼지션에서 나오는 엔터티 형식이 포함되어 있으면 EF Core가 이를 추적합니다.If the result set contains entity types coming out from LINQ composition, EF Core will track them.

var blog = context.Blogs
    .Select(b =>
        new
        {
            Blog = b,
            Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault()
        });

결과 집합에 포함된 엔터티 형식이 없으면 추적이 수행되지 않습니다.If the result set doesn't contain any entity types, then no tracking is done. 다음 쿼리에서는 엔터티의 값 일부가 있는 무명 형식을 반환합니다(하지만 실제 엔터티 형식의 인스턴스는 반환하지 않습니다).In the following query, we return an anonymous type with some of the values from the entity (but no instances of the actual entity type). 쿼리에서 나오는 추적되는 엔터티는 없습니다.There are no tracked entities coming out of the query.

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

EF Core는 최상위 프로젝션에서의 클라이언트 평가 수행을 지원합니다.EF Core supports doing client evaluation in the top-level projection. EF Core가 클라이언트 평가를 위해 엔터티 인스턴스를 구체화하는 경우 이 엔터티 인스턴스가 추적됩니다.If EF Core materializes an entity instance for client evaluation, it will be tracked. 여기서는 클라이언트 메서드 StandardizeURLblog 엔터티를 전달하므로 EF Core는 블로그 인스턴스도 추적합니다.Here, since we're passing blog entities to the client method StandardizeURL, EF Core will track the blog instances too.

var blogs = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(blog => new
    {
        Id = blog.BlogId,
        Url = StandardizeUrl(blog)
    })
    .ToList();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

EF Core는 결과에 포함된 키 없는 엔터티 인스턴스를 추적하지 않습니다.EF Core doesn't track the keyless entity instances contained in the result. 그러나 EF Core는 위의 규칙에 따라 키가 있는 엔터티 형식의 다른 인스턴스를 모두 추적합니다.But EF Core tracks all the other instances of entity types with key according to rules above.

위의 규칙 일부는 EF Core 3.0 이전에는 다르게 작동했습니다.Some of the above rules worked differently before EF Core 3.0. 자세한 내용은 이전 버전을 참조하세요.For more information, see previous versions.

이전 버전Previous versions

버전 3.0 이전에는 EF Core의 추적 수행 방법에 약간의 차이가 있었습니다.Before version 3.0, EF Core had some differences in how tracking was done. 주목할 만한 차이점은 다음과 같습니다.Notable differences are as follows:

  • 클라이언트 및 서버 평가 페이지에 설명한 것처럼 버전 3.0 이전에는 EF Core가 쿼리의 모든 부분에서 클라이언트 평가를 지원했습니다.As explained in Client vs Server Evaluation page, EF Core supported client evaluation in any part of the query before version 3.0. 클라이언트 평가로 인해 결과에 포함되지 않는 엔터티의 구체화가 수행되었습니다.Client evaluation caused materialization of entities, which weren't part of the result. 따라서 EF Core는 결과를 분석하여 추적할 항목을 검색했습니다. 이 설계에는 다음과 같은 몇 가지 차이점이 있었습니다.So EF Core analyzed the result to detect what to track. This design had certain differences as follows:

    • 구체화를 초래했지만 구체화된 엔터티 인스턴스를 반환하지 않은 프로젝션의 클라이언트 평가는 추적되지 않았습니다.Client evaluation in the projection, which caused materialization but didn't return the materialized entity instance wasn't tracked. 다음 예는 blog 엔터티를 추적하지 않았습니다.The following example didn't track blog entities.

      var blogs = context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(blog => new
          {
              Id = blog.BlogId,
              Url = StandardizeUrl(blog)
          })
          .ToList();
      
    • EF Core는 특정한 경우에 LINQ 컴퍼지션에서 나오는 개체를 추적하지 않았습니다.EF Core didn't track the objects coming out of LINQ composition in certain cases. 다음 예는 Post를 추적하지 않았습니다.The following example didn't track Post.

      var blog = context.Blogs
          .Select(b =>
              new
              {
                  Blog = b,
                  Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault()
              });
      
  • 쿼리 결과에 키 없는 엔터티 형식이 포함될 때마다 전체 쿼리가 비 추적 쿼리로 전환되었습니다.Whenever query results contained keyless entity types, the whole query was made non-tracking. 즉, 키가 있고 결과에 포함된 엔터티 형식도 추적되지 않았습니다.That means that entity types with keys, which are in result weren't being tracked either.

  • EF Core는 비 추적 쿼리에서 ID 확인을 수행했습니다.EF Core did identity resolution in no-tracking query. EF Core는 약한 참조를 사용하여 이미 반환된 엔터티를 추적했습니다.It used weak references to keep track of entities that had already been returned. 따라서 결과 집합에 동일한 엔터티가 여러 번 포함된 경우 발생할 때마다 동일한 인스턴스를 가져옵니다.So if a result set contained the same entity multiples times, you would get the same instance for each occurrence. ID가 동일한 이전 결과가 범위를 벗어나고 가비지가 수집된 경우에도 EF Core는 새 인스턴스를 반환했습니다.Though if a previous result with the same identity went out of scope and got garbage collected, EF Core returned a new instance.