관련 데이터의 즉시 로드

즉시 로드

Include 메서드를 사용하여 쿼리 결과에 포함할 관련 데이터를 지정할 수 있습니다. 다음 예제에서 결과에 반환되는 블로그에는 관련 게시물로 채워진 Posts 속성이 있습니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ToList();
}

Entity Framework Core는 이전에 컨텍스트 인스턴스에 로드된 다른 엔터티로 탐색 속성을 자동으로 수정합니다. 따라서 탐색 속성에 대한 데이터를 명시적으로 포함하지 않더라도 관련 엔터티의 일부 또는 전체가 이전에 로드된 경우 속성이 채워질 수 있습니다.

여러 관계의 관련 데이터를 단일 쿼리에 포함할 수 있습니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Include(blog => blog.Owner)
        .ToList();
}

주의

단일 쿼리에서 컬렉션 탐색을 즉시 로드하면 성능 문제가 발생할 수 있습니다. 자세한 내용은 단일 쿼리와 분할 쿼리 비교를 참조하세요.

여러 수준 포함

ThenInclude 메서드를 사용하여 여러 수준의 관련 데이터를 포함하도록 관계를 드릴다운할 수 있습니다. 다음 예제에서는 모든 블로그, 관련 게시물 및 각 게시물의 작성자를 로드합니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();
}

ThenInclude에 대한 여러 호출을 연결하여 관련 데이터의 추가 수준을 계속 포함할 수 있습니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .ToList();
}

호출을 모두 결합하여 여러 수준 및 여러 루트의 관련 데이터를 동일한 쿼리에 포함할 수 있습니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .Include(blog => blog.Owner)
        .ThenInclude(owner => owner.Photo)
        .ToList();
}

포함하려는 엔터티 중 하나에 대한 여러 관련 엔터티를 포함할 수 있습니다. 예를 들어 Blogs를 쿼리하는 경우 Posts를 포함한 다음, PostsAuthorTags를 모두 포함할 수 있습니다. 둘 다 포함하려면 루트에서 시작하는 각 포함 경로를 지정해야 합니다. 예를 들어 Blog -> Posts -> AuthorBlog -> Posts -> Tags를 지정합니다. 이는 중복 조인을 가져온다는 뜻이 아니며, 대부분의 경우 EF는 SQL을 생성할 때 조인을 결합합니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToList();
}

단일 Include 메서드를 사용하여 여러 탐색을 로드할 수도 있습니다. 이는 모두 참조인 탐색 “체인”의 경우나 단일 컬렉션으로 끝나는 경우에 가능합니다.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Owner.AuthoredPosts)
        .ThenInclude(post => post.Blog.Owner.Photo)
        .ToList();
}

필터링 적용 포함

Include를 적용하여 관련 데이터를 로드할 경우 포함된 컬렉션 탐색에 특정 열거 가능 작업을 추가하여 결과를 필터링 및 정렬할 수 있습니다.

지원되는 작업은 Where, OrderBy, OrderByDescending, ThenBy, ThenByDescending, SkipTake입니다.

이러한 작업은 아래 예제와 같이 Include 메서드에 전달된 람다의 컬렉션 탐색에 적용해야 합니다.

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

포함된 각 탐색은 하나의 고유한 필터 작업 집합만 허용합니다. 지정된 컬렉션 탐색에 여러 Include 작업이 적용되는 경우(아래 예제의 blog.Posts) 필터 작업은 다음 중 하나에만 지정할 수 있습니다.

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

대신 여러 번 포함되는 각 탐색에 동일한 작업을 적용할 수 있습니다.

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

주의

쿼리 추적의 경우 탐색 수정으로 인해 필터링된 Include에서 예기치 않은 결과가 발생할 수 있습니다. 이전에 쿼리되고 변경 추적 장치에 저장된 모든 관련 엔터티는 필터 요구 사항을 충족하지 않는 경우에도 필터링된 Include 쿼리 결과에 표시됩니다. 이러한 상황에서 필터링된 Include를 사용하는 경우 NoTracking 쿼리를 사용하거나 DbContext를 다시 만드는 것이 좋습니다.

예:

var orders = context.Orders.Where(o => o.Id > 1000).ToList();

// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = context.Customers.Include(c => c.Orders.Where(o => o.Id > 5000)).ToList();

참고

추적 쿼리의 경우 필터링된 Include가 적용된 탐색은 로드될 것으로 간주됩니다. 즉, EF Core는 일부 요소가 여전히 없더라도 명시적 로드 또는 지연 로드를 사용하여 해당 값을 다시 로드하려고 시도하지 않습니다.

파생 형식에 포함

IncludeThenInclude를 사용하여 정의된 탐색의 관련 데이터를 파생 형식에만 포함할 수 있습니다.

다음과 같은 모델을 가정합니다.

public class SchoolContext : DbContext
{
    public DbSet<Person> People { get; set; }
    public DbSet<School> Schools { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<School>().HasMany(s => s.Students).WithOne(s => s.School);
    }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Student : Person
{
    public School School { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Student> Students { get; set; }
}

여러 패턴을 사용하여 학생인 모든 사람의 School 탐색 콘텐츠를 로드할 수 있습니다.

  • 캐스트 사용

    context.People.Include(person => ((Student)person).School).ToList()
    
  • as 연산자 사용

    context.People.Include(person => (person as Student).School).ToList()
    
  • string 형식의 매개 변수를 사용하는 Include의 오버로드 사용

    context.People.Include("School").ToList()
    

자동 포함 탐색 모델 구성

AutoInclude 메서드를 사용하여 데이터베이스에서 엔터티를 로드할 때마다 포함되도록 모델의 탐색을 구성할 수 있습니다. 엔터티 형식이 결과에 반환되는 모든 쿼리에서 탐색에 Include를 지정하는 것과 동일한 효과가 있습니다. 다음 예제는 탐색이 자동으로 포함되도록 구성하는 방법을 보여 줍니다.

modelBuilder.Entity<Theme>().Navigation(e => e.ColorScheme).AutoInclude();

위 구성 후에 아래와 같은 쿼리를 실행하면 결과에 모든 테마의 ColorScheme 탐색이 로드됩니다.

using (var context = new BloggingContext())
{
    var themes = context.Themes.ToList();
}

이 구성은 결과에 표시되는 방식에 관계없이 결과에 반환된 모든 엔터티에 적용됩니다. 즉, 다른 엔터티 형식 또는 자동 포함 구성에 대해 Include를 사용하여 엔터티가 탐색 사용으로 인한 결과에 포함되는 경우 해당 엔터티에 대한 모든 자동 포함 탐색이 로드됩니다. 동일한 규칙이 엔터티의 파생된 형식에 자동 포함으로 구성된 탐색으로 확장됩니다.

모델 수준에서 자동 포함되도록 구성된 탐색을 통해 관련 데이터를 로드하지 않으려는 특정 쿼리의 경우 쿼리에서 IgnoreAutoIncludes 메서드를 사용할 수 있습니다. 이 메서드를 사용하면 사용자가 자동 포함으로 구성한 모든 탐색 로드가 중지됩니다. 아래와 같은 쿼리를 실행하면 데이터베이스에서 모든 테마를 다시 가져오지만 자동 포함 탐색으로 구성된 경우에도 ColorScheme은 로드되지 않습니다.

using (var context = new BloggingContext())
{
    var themes = context.Themes.IgnoreAutoIncludes().ToList();
}

참고

소유한 형식에 대한 탐색도 규칙에 따라 자동 포함으로 구성되며, IgnoreAutoIncludes API를 사용해도 테마가 포함되는 것이 중지되지 않습니다. 쿼리 결과에 계속 포함됩니다.