다음을 통해 공유


추적 및 No-Tracking 쿼리

Entity Framework Core가 엔터티 인스턴스에 대한 정보를 변경 추적기에 유지할지 여부를 결정하는 것이 추적 동작입니다. 엔터티를 추적하는 경우 엔터티에서 검색된 모든 변경 내용은 그 동안 SaveChanges데이터베이스에 유지됩니다. 또한 EF Core는 추적 쿼리 결과의 엔터티와 변경 추적기에서 엔터티 간의 탐색 속성을 수정합니다.

비고

키 없는 엔터티 형식 은 추적되지 않습니다. 이 문서에서 엔터티 형식을 언급할 때마다 키가 정의된 엔터티 형식을 참조합니다.

팁 (조언)

GitHub에서 이 문서의 샘플을 볼 수 있습니다.

쿼리 추적

기본적으로 엔터티 형식을 반환하는 쿼리는 추적됩니다. 추적 쿼리는 엔터티 인스턴스에 대한 모든 변경 사항이 SaveChanges에 의해 저장됨을 의미합니다. 다음 예제에서는 블로그 등급 변경 내용이 검색되어 데이터베이스에 유지됩니다.SaveChanges

var blog = await context.Blogs.SingleOrDefaultAsync(b => b.BlogId == 1);
blog.Rating = 5;
await context.SaveChangesAsync();

추적 쿼리에서 결과가 반환되면 EF Core는 엔터티가 이미 컨텍스트에 있는지 확인합니다. EF Core가 기존 엔터티를 찾으면 동일한 인스턴스가 반환되므로 메모리를 적게 사용하고 추적하지 않는 쿼리보다 더 빠를 수 있습니다. EF Core는 항목에서 엔터티 속성의 현재 및 원래 값을 데이터베이스 값으로 덮어쓰지 않습니다. 컨텍스트에서 엔터티를 찾을 수 없는 경우 EF Core는 새 엔터티 인스턴스를 만들고 컨텍스트에 연결합니다. 쿼리 결과에는 컨텍스트에 추가되었지만 데이터베이스에 아직 저장되지 않은 엔터티가 포함되지 않습니다.

추적하지 않는 쿼리

비 추적 쿼리는 읽기 전용 시나리오에서 결과가 사용되는 경우에 유용합니다. 이 쿼리는 변경 내용 추적 정보를 설정할 필요가 없기 때문에 더 빠르게 실행할 수 있습니다. 데이터베이스에서 검색된 엔터티를 업데이트할 필요가 없는 경우 추적 금지 쿼리를 사용해야 합니다. 개별 쿼리는 추적 없음으로 설정할 수 있습니다. 추적 없음 쿼리는 로컬 변경 내용 또는 추가된 엔터티를 무시하고 데이터베이스의 내용을 기반으로 결과를 제공합니다.

var blogs = await context.Blogs
    .AsNoTracking()
    .ToListAsync();

기본 추적 동작은 컨텍스트 인스턴스 수준에서 변경할 수 있습니다.

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = await context.Blogs.ToListAsync();

다음 섹션에서는 추적 없음 쿼리가 추적 쿼리보다 덜 효율적일 수 있는 경우를 설명합니다.

신원 해소

추적 쿼리는 변경 추적기를 사용하므로 EF Core는 추적 쿼리에서 ID 확인을 수행합니다. 엔터티를 구체화할 때 EF Core는 이미 추적 중인 경우 변경 추적기에서 동일한 엔터티 인스턴스를 반환합니다. 결과에 동일한 엔터티가 여러 번 포함된 경우 각 항목에 대해 동일한 인스턴스가 반환됩니다. 추적 없는 쿼리:

  • 변경 추적기를 사용하지 말고 ID 확인을 수행하지 마세요.
  • 동일한 엔터티가 결과에 여러 번 포함된 경우에도 엔터티의 새 인스턴스를 반환합니다.

추적 및 추적 없음은 동일한 쿼리에서 결합할 수 있습니다. 즉, 결과에서 ID 확인을 수행하는 추적 없음 쿼리를 사용할 수 있습니다. 쿼리 가능한 연산자처럼, 우리는 또 다른 연산자 AsNoTracking를 추가했습니다. 열거형에 연결된 항목이 QueryTrackingBehavior에도 추가되었습니다. ID 확인을 사용하는 쿼리가 추적 없이 구성된 경우 각 인스턴스가 한 번만 구체화되도록 쿼리 결과를 생성할 때 독립 실행형 변경 추적기가 백그라운드에서 사용됩니다. 이 변경 추적기는 컨텍스트의 변경 내용과 다르므로 컨텍스트에서 결과를 추적하지 않습니다. 쿼리가 완전히 열거되면, 변경 추적기가 범위에서 벗어나게 되고 필요에 따라 가비지 수집이 이루어진다.

var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToListAsync();

기본 추적 동작 구성

많은 쿼리에 대한 추적 동작을 변경하는 경우 대신 기본값을 변경할 수 있습니다.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True;ConnectRetryCount=0")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

이렇게 하면 모든 쿼리가 기본적으로 추적되지 않습니다. 여전히 AsTracking을 추가하여 특정 쿼리를 추적할 수 있습니다.

추적 및 사용자 지정 프로젝션

쿼리의 결과 형식이 엔터티 형식이 아니더라도 EF Core는 기본적으로 결과에 포함된 엔터티 형식을 추적합니다. 익명 형식을 반환하는 다음 쿼리에서 결과 집합의 Blog 인스턴스가 추적됩니다.

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

결과 집합에 LINQ 컴퍼지션에서 나오는 엔터티 형식이 포함된 경우 EF Core는 이를 추적합니다.

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

결과 집합에 엔터티 형식이 없으면 추적이 수행되지 않습니다. 다음 쿼리에서는 엔터티의 일부 값이 포함된 익명 형식을 반환합니다(실제 엔터티 형식의 인스턴스는 없음). 쿼리에서 나오는 추적된 엔터티가 없습니다.

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

EF Core는 최상위 프로젝션에서 클라이언트 평가 작업을 지원합니다. EF Core가 클라이언트 평가를 위해 엔터티 인스턴스를 구체화하면 추적됩니다. 여기서는 blog 엔터티를 클라이언트 메서드 StandardizeURL에 전달하기 때문에, EF Core도 블로그 인스턴스를 추적합니다.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToListAsync();
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는 위의 규칙에 따라 키를 사용하여 엔터티 형식의 다른 모든 인스턴스를 추적합니다.

이전 버전

버전 3.0 이전에는 EF Core에서 추적이 수행되는 방식에 몇 가지 차이가 있었습니다. 주목할 만한 차이점은 다음과 같습니다.

  • 클라이언트 및 서버 평가 페이지에 설명된 대로 EF Core는 버전 3.0 이전의 쿼리 부분에서 클라이언트 평가를 지원했습니다. 고객 평가로 인해 엔터티가 실체화되었지만 결과에 포함되지 않았습니다. 따라서 EF Core는 결과를 분석하여 추적할 내용을 검색했습니다. 이 디자인에는 다음과 같은 특정 차이점이 있습니다.

    • 구체화를 발생시켰지만 구체화된 엔터티 인스턴스를 반환하지 않은 프로젝션의 클라이언트 평가는 추적되지 않았습니다. 다음 예제에서는 blog 엔터티를 추적하지 않았습니다.

      var blogs = await context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToListAsync();
      
    • EF Core는 특정 경우에 LINQ 컴퍼지션에서 나오는 개체를 추적하지 않았습니다. 다음 예제에서 Post를 추적하지 않았습니다.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • 쿼리 결과에 키가 없는 엔터티 형식이 포함될 때마다 전체 쿼리가 추적되지 않습니다. 즉, 결과에 있는 키가 있는 엔터티 형식도 추적되지 않았습니다.

  • 추적 없음 쿼리에서 ID 확인을 수행하는 데 사용되는 EF Core입니다. 약한 참조를 사용하여 이미 반환된 엔터티를 추적했습니다. 따라서 결과 집합에 동일한 엔터티가 여러 번 포함된 경우 각 항목에 대해 동일한 인스턴스를 가져옵니다. 동일한 ID를 가진 이전 결과가 범위를 벗어나 가비지를 수집한 경우 EF Core는 새 인스턴스를 반환했습니다.