다음을 통해 공유


쿼리 결과 셰이핑(Entity Framework)

쿼리를 실행하면 쿼리에서 특정하게 요청된 개체만 반환됩니다. 예를 들어 Adventure Works Sales 모델에 대한 쿼리 결과로 Customer 개체가 반환될 때, CustomerSalesOrderHeader 간에 관계가 있는 경우라도 기본적으로 관련 SalesOrderHeader 개체가 반환되지 않습니다. 이런 동작으로 인해 응용 프로그램은 언제든지 개체 쿼리에서 반환되는 데이터의 범위를 인식하게 됩니다. 기본적으로, 엔터티 형식 간의 관계를 나타내는 관계 개체가 항상 반환됩니다. EDM의 개념 스키마를 기반으로 개체가 생성되면 엔터티 개체에 대한 탐색 속성이 연결의 양 End에 생성됩니다. 이러한 탐색 속성은 일대일 또는 다대일 관계의 "한 쪽" End에서 EntityReference를 반환하거나 일대다 또는 다대다 관계의 "다수의" End에서 EntityCollection을 반환합니다. 자세한 내용은 엔터티 데이터 모델 관계를 참조하십시오.

이러한 관계를 탐색 속성을 통해 명시적으로 탐색하는 Entity SQL 또는 LINQ to Entities 쿼리를 작성할 수 있습니다. 자세한 내용은 방법: 탐색 속성을 사용하여 관계 탐색(Entity Framework)을 참조하십시오. 하지만, 쿼리 결과 셰이핑을 위해 쿼리에서 명시적으로 관계를 탐색할 필요는 없습니다. 참조된 개체도 로드하도록 쿼리 결과를 확장하는 데는 다른 두 가지 방법이 있습니다. 쿼리 경로를 지정하는 방법이 있고, 탐색 속성을 사용하여 관련 개체를 명시적으로 로드할 수도 있습니다. 결과에 대한 제어를 강화하려면 쿼리 경로를 정의한 다음 선택된 관련 개체만 명시적으로 로드할 수 있습니다.

사용할 옵션을 고려할 때는 데이터베이스에 대한 요청의 개수와 단일 쿼리에 반환되는 데이터의 양이 서로 상쇄되는 관계임을 염두에 두어야 합니다. 쿼리 경로는 쿼리가 반환하는 개체의 그래프를 정의합니다. 쿼리 경로를 정의하면 데이터베이스에 대한 요청 하나만이 경로에 정의된 모든 개체를 단일 결과 집합으로 반환하면 됩니다. 개체를 명시적으로 로드하려면 데이터베이스를 대상으로 한 라운드트립이 여러 번 필요하며 MARS(Multiple Active Result Set)가 요구될 수도 있지만, 반환되는 데이터의 양은 로드되는 개체로 제한됩니다.

쿼리 경로를 정의하여 쿼리 결과 셰이핑

쿼리 경로를 지정하려면 문자열로 나타낸 개체 그래프를 ObjectQueryInclude 메서드에 전달합니다. 이 경로는 개체 쿼리 실행 시 반환될 관련 개체를 지정합니다. 예를 들어, Contact 개체에 대한 쿼리에서 쿼리 경로를 정의하면 개별 관련 SalesOrderHeaderSalesOrderDetail이 반환됩니다. 이는 LINQ to Entities, Entity SQL 및 쿼리 작성기 메서드를 사용한 다음 쿼리에서 확인할 수 있습니다.

  • LINQ to Entities
``` vb
' Define a LINQ query with a path that returns 
' orders and items for a contact.
Dim contacts = (From contact In context.Contact _
    .Include("SalesOrderHeader.SalesOrderDetail") _
    Select contact).FirstOrDefault()
```

``` csharp
// Define a LINQ query with a path that returns 
// orders and items for a contact.
var contacts = (from contact in context.Contact
              .Include("SalesOrderHeader.SalesOrderDetail")
              select contact).FirstOrDefault();
```
  • Entity SQL
``` vb
' Define an object query with a path that returns 
' orders and items for a specific contact.              
Dim queryString As String = _
    "SELECT VALUE TOP(1) Contact FROM " + _
    "AdventureWorksEntities.Contact AS Contact"

' Define the object query with the query string.
Dim contactQuery As New ObjectQuery(Of Contact)(queryString, _
    context, MergeOption.NoTracking)

Dim contact As Contact = _
contactQuery.Include("SalesOrderHeader.SalesOrderDetail") _
    .FirstOrDefault()
```

``` csharp
// Define an object query with a path that returns 
// orders and items for a specific contact.              
string queryString =
    @"SELECT VALUE TOP(1) Contact FROM " + 
    "AdventureWorksEntities.Contact AS Contact";

// Define the object query with the query string.
ObjectQuery<Contact> contactQuery = new ObjectQuery<Contact>(queryString, 
    context, MergeOption.NoTracking);

Contact contact =
    contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
    .FirstOrDefault();
```
  • 쿼리 작성기 메서드

    ' Create an object query with a path that returns orders and items for a contact.
    Dim contact As Contact = _
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.
    Contact contact =
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();
    

쿼리 경로를 정의할 때는 다음 사항을 고려해야 합니다.

  • 쿼리 경로는 쿼리 작성기 메서드 및 LINQ 쿼리에 사용할 수 있습니다.

  • Include를 호출하는 경우 쿼리 경로는 ObjectQuery의 반환된 인스턴스에만 사용할 수 있습니다. ObjectQuery의 다른 인스턴스와 개체 컨텍스트 자체에는 영향을 주지 않습니다.

  • Include는 쿼리 개체를 반환하므로 이 메서드를 ObjectQuery에 대해 여러 번 호출하여 여러 관계의 개체를 포함할 수 있습니다. 예를 들면 다음과 같습니다.

    ' Create a SalesOrderHeader query with two query paths, 
    ' one that returns order items and a second that returns the 
    ' billing and shipping addresses for each order.
    Dim query As ObjectQuery(Of SalesOrderHeader) = _
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address")
    
    // Create a SalesOrderHeader query with two query paths, 
    // one that returns order items and a second that returns the 
    // billing and shipping addresses for each order.
    ObjectQuery<SalesOrderHeader> query =
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address");
    
  • 쿼리 경로를 사용하면 단순 개체 쿼리의 데이터 원본에 대해 복잡한 명령이 실행될 수 있습니다. 이는 관련 개체를 단일 쿼리에서 반환하려면 조인이 하나 이상 필요하기 때문에 발생합니다. 이런 복잡성은 다대다 관계가 포함된 상속이나 경로를 가진 엔터티와 같은 복합 EDM에 대한 쿼리에서 더 커집니다 ObjectQuery에서 생성되는 명령을 보려면 ToTraceString 메서드를 사용하십시오. 자세한 내용은 개체 쿼리(Entity Framework)를 참조하십시오. 쿼리 경로에 관련 개체가 너무 많거나 개체에 행 데이터가 너무 많은 경우, 데이터 원본에서 쿼리 처리가 완료되지 못할 수 있습니다. 쿼리에 필요한 중간 임시 저장소가 데이터 원본의 용량을 초과하는 경우 이런 현상이 발생할 수 있습니다. 이 경우, 관련 개체를 명시적으로 로드하는 방법으로 데이터 원본 쿼리의 복잡성을 줄일 수 있습니다.

자세한 내용은 방법: 쿼리 경로를 사용하여 결과 모양 결정(Entity Framework)을 참조하십시오.

명시적으로 관련 개체 로드

관련 개체를 명시적으로 로드하려면 탐색 속성을 통해 반환된 관련 End에서 Load 메서드를 호출해야 합니다. 일대다 관계의 경우 EntityCollection에 대해 Load 메서드를 호출하고, 일대일 관계의 경우 EntityReference에 대해 Load를 호출합니다. 이렇게 하면 관련 개체 데이터가 개체 컨텍스트에 로드됩니다. 쿼리에서 개체 컬렉션이 반환되면 컬렉션 전체를 열거하고 Load 메서드를 호출하여 컬렉션의 각 개체에 대한 관련 개체(예: SalesOrderHeader 개체에 속한 각 SalesOrderDetail 개체)를 로드할 수 있습니다. 다음 예제에서는 지정된 SalesOrderHeader 개체에 대해 SalesOrderDetail 개체가 명시적으로 로드됩니다.

' Load the items for the order if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
    order.SalesOrderDetail.Load()
End If
// Load the items for the order if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
    order.SalesOrderDetail.Load();
}
Note참고

foreach(C#) 또는 For Each(Visual Basic) 열거를 수행하는 동안 Load 메서드를 호출하면 개체 서비스에서 새 데이터 판독기를 열려고 시도합니다. 연결 문자열에서 multipleactiveresultsets=true를 지정하여 MARS(Multiple Active Result Sets)를 활성화하지 않은 경우에는 이 작업이 실패합니다. 자세한 내용은 MSDN에서 Using Multiple Active Result Sets (MARS)를 참조하십시오. 쿼리 결과를 List 컬렉션에 로드할 수도 있으며, 이렇게 하면 데이터 판독기가 닫히고 참조된 개체를 로드할 수 있게 됩니다.

자세한 내용은 방법: 명시적으로 관련 개체 로드(Entity Framework)를 참조하십시오.

관련 개체 쿼리

EntityCollection 클래스에서 IEnumerable 인터페이스가 구현되므로, 탐색 속성을 통해 반환된 EntityCollection에 로드된 개체 컬렉션을 쿼리하는 데 LINQ를 사용할 수 있습니다. 쿼리 경로를 지정하는 방법으로 개체를 개체 컨텍스트에 암시적으로 로드하거나 Load 메서드를 호출하는 방법으로 개체를 명시적으로 로드하면 됩니다.

EntityCollection에 대해 CreateSourceQuery 메서드를 호출하면 먼저 개체를 컬렉션에 로드하지 않고도 관련 개체를 쿼리할 수 있습니다. CreateSourceQueryObjectQuery를 반환하며, 이를 실행하면 Load 메서드를 호출했을 때와 동일한 개체 집합이 반환됩니다. 쿼리 작성기 메서드를 이 개체 쿼리에 적용하여 컬렉션에 로드할 개체를 추가로 필터링할 수 있습니다. 자세한 내용은 방법: EntityCollection의 쿼리 관련 개체(Entity Framework)를 참조하십시오.

ObjectQuery는 EDM 데이터를 엔터티 개체 형태로 반환합니다. 하지만, 쿼리 프로젝션에 탐색 속성이 포함되어 있으면 ObjectQuery는 관련 개체가 포함된 중첩 DbDataRecord를 반환합니다. 자세한 내용은 방법: 탐색 속성을 사용하여 관계 탐색(Entity Framework)을 참조하십시오.

참고 항목

개념

데이터를 개체로 쿼리(Entity Framework)
개체 쿼리(Entity Framework)
쿼리 작성기 메서드(Entity Framework)