ASP.NET 4 웹 응용 프로그램에서 Entity Framework 4.0를 사용 하 여 성능 최대화Maximizing Performance with the Entity Framework 4.0 in an ASP.NET 4 Web Application

만든 사람 Tom Dykstraby Tom Dykstra

이 자습서 시리즈는 Entity Framework 4.0 자습서 시리즈 시작에서 만든 Contoso 대학 웹 응용 프로그램을 기반으로 합니다.This tutorial series builds on the Contoso University web application that is created by the Getting Started with the Entity Framework 4.0 tutorial series. 이전 자습서를 완료 하지 않은 경우이 자습서의 시작 점으로, 만든 응용 프로그램을 다운로드할 수 있습니다.If you didn't complete the earlier tutorials, as a starting point for this tutorial you can download the application that you would have created. 전체 자습서 시리즈에서 만든 응용 프로그램을 다운로드할 수도 있습니다.You can also download the application that is created by the complete tutorial series. 자습서에 대 한 질문이 있는 경우 ASP.NET Entity Framework 포럼에 게시할 수 있습니다.If you have questions about the tutorials, you can post them to the ASP.NET Entity Framework forum.

이전 자습서에서 동시성 충돌을 처리 하는 방법을 살펴보았습니다.In the previous tutorial, you saw how to handle concurrency conflicts. 이 자습서에서는 Entity Framework를 사용 하는 ASP.NET 웹 응용 프로그램의 성능을 향상 시키기 위한 옵션을 보여 줍니다.This tutorial shows options for improving the performance of an ASP.NET web application that uses the Entity Framework. 성능을 최대화 하거나 성능 문제를 진단 하기 위한 몇 가지 방법을 알아봅니다.You'll learn several methods for maximizing performance or for diagnosing performance problems.

다음 섹션에 나와 있는 정보는 다양 한 시나리오에서 유용할 수 있습니다.Information presented in the following sections is likely to be useful in a broad variety of scenarios:

  • 관련 데이터를 효율적으로 로드 합니다.Efficiently load related data.
  • 뷰 상태를 관리 합니다.Manage view state.

다음 섹션에 나와 있는 정보는 성능 문제를 발생 시키는 개별 쿼리가 있는 경우에 유용할 수 있습니다.Information presented in the following sections might be useful if you have individual queries that present performance problems:

  • NoTracking 병합 옵션을 사용 합니다.Use the NoTracking merge option.
  • LINQ 쿼리를 미리 컴파일합니다.Pre-compile LINQ queries.
  • 데이터베이스에 전송 된 쿼리 명령을 검사 합니다.Examine query commands sent to the database.

다음 섹션에 나와 있는 정보는 매우 큰 데이터 모델을 가진 응용 프로그램에 유용할 수 있습니다.Information presented in the following section is potentially useful for applications that have extremely large data models:

  • 뷰를 미리 생성 합니다.Pre-generate views.

Note

웹 응용 프로그램 성능에는 요청 및 응답 데이터의 크기, 데이터베이스 쿼리의 속도, 서버에서 큐에 대기 시킬 수 있는 요청 수, 서비스의 효율성 등 여러 가지 요소가 영향을 받습니다. 클라이언트 스크립트 라이브러리를 사용 하 고 있을 수 있습니다.Web application performance is affected by many factors, including things like the size of request and response data, the speed of database queries, how many requests the server can queue and how quickly it can service them, and even the efficiency of any client-script libraries you might be using. 응용 프로그램에서 성능이 중요 한 경우 또는 테스트 또는 환경에서 응용 프로그램 성능이 만족 스 럽 지 않은 것으로 표시 되는 경우 성능 조정을 위해 일반 프로토콜을 따라야 합니다.If performance is critical in your application, or if testing or experience shows that application performance isn't satisfactory, you should follow normal protocol for performance tuning. 측정 하 여 성능 병목 현상이 발생 하는 위치를 확인 한 다음 전체 응용 프로그램 성능에 가장 큰 영향을 줄 수 있는 영역을 해결 합니다.Measure to determine where performance bottlenecks are occurring, and then address the areas that will have the greatest impact on overall application performance.

이 항목에서는 ASP.NET에서 특히 Entity Framework의 성능을 향상 시킬 수 있는 방법에 중점을 둘 수 있습니다.This topic focuses mainly on ways in which you can potentially improve the performance specifically of the Entity Framework in ASP.NET. 이 제안 사항은 데이터 액세스가 응용 프로그램의 성능 병목 현상 중 하나 인지 확인 하는 경우에 유용 합니다.The suggestions here are useful if you determine that data access is one of the performance bottlenecks in your application. 설명 된 경우를 제외 하 고, 여기에 설명 된 메서드는 일반적으로" "모범 사례에 따라 고려 하지 않아야 합니다. 대부분은 예외적인 상황 에서만 적절 하거나 특정 유형의 성능 병목 현상을 해결할 수 있습니다.Except as noted, the methods explained here shouldn't be considered "best practices" in general — many of them are appropriate only in exceptional situations or to address very specific kinds of performance bottlenecks.

자습서를 시작 하려면 Visual Studio를 시작 하 고 이전 자습서에서 작업 하 던 Contoso 대학 웹 응용 프로그램을 엽니다.To start the tutorial, start Visual Studio and open the Contoso University web application that you were working with in the previous tutorial.

Entity Framework는 엔터티의 탐색 속성에 관련 데이터를 로드할 수 있는 여러 가지 방법이 있습니다.There are several ways that the Entity Framework can load related data into the navigation properties of an entity:

  • 지연 로드.Lazy loading. 엔터티를 처음 읽을 때 관련된 데이터가 검색되지 않습니다.When the entity is first read, related data isn't retrieved. 그러나 탐색 속성에 처음으로 액세스하려고 할 때 해당 탐색 속성에 필요한 데이터가 자동으로 검색됩니다.However, the first time you attempt to access a navigation property, the data required for that navigation property is automatically retrieved. 이렇게 하면 여러 개의 쿼리가 데이터베이스에 전송 됩니다. 하나는 엔터티 자체를 위한 것이 고 다른 하나는 엔터티에 대 한 관련 데이터를 검색 해야 하는 경우입니다.This results in multiple queries sent to the database — one for the entity itself and one each time that related data for the entity must be retrieved.

    Image05Image05

즉시 로드.Eager loading. 엔터티를 읽을 때 관련된 데이터가 함께 검색됩니다.When the entity is read, related data is retrieved along with it. 이는 일반적으로 필요한 데이터를 모두 검색하는 단일 조인 쿼리를 발생시킵니다.This typically results in a single join query that retrieves all of the data that's needed. 이러한 자습서에서 살펴본 것 처럼 Include 메서드를 사용 하 여 즉시 로드를 지정 합니다.You specify eager loading by using the Include method, as you've seen already in these tutorials.

Image07Image07

  • 명시적 로드.Explicit loading. 코드에서 관련 데이터를 명시적으로 검색 하는 경우를 제외 하 고는 지연 로드와 비슷합니다. 탐색 속성에 액세스 하는 경우 자동으로 발생 하지 않습니다.This is similar to lazy loading, except that you explicitly retrieve the related data in code; it doesn't happen automatically when you access a navigation property. 컬렉션에 대 한 탐색 속성의 Load 메서드를 사용 하 여 관련 데이터를 수동으로 로드 하거나, 단일 개체를 포함 하는 속성에 대 한 참조 속성의 Load 메서드를 사용 합니다.You load related data manually using the Load method of the navigation property for collections, or you use the Load method of the reference property for properties that hold a single object. 예를 들어 PersonReference.Load 메서드를 호출 하 여 Department 엔터티의 Person 탐색 속성을 로드 합니다.(For example, you call the PersonReference.Load method to load the Person navigation property of a Department entity.)

    Image06Image06

속성 값을 즉시 검색 하지 않으므로 지연 로드 및 명시적 로드도 모두 지연 된 로드라고도 합니다.Because they don't immediately retrieve the property values, lazy loading and explicit loading are also both known as deferred loading.

지연 로드는 디자이너가 생성 한 개체 컨텍스트의 기본 동작입니다.Lazy loading is the default behavior for an object context that has been generated by the designer. 개체 컨텍스트 클래스를 정의 하는 SchoolModel.Designer.cs 파일을 여는 경우 세 가지 생성자 메서드를 찾을 수 있으며 각 메서드는 다음 문을 포함 합니다.If you open the SchoolModel.Designer.cs file that defines the object context class, you'll find three constructor methods, and each of them includes the following statement:

this.ContextOptions.LazyLoadingEnabled = true;

일반적으로 검색 되는 모든 엔터티에 대 한 관련 데이터가 필요 하다는 것을 알고 있는 경우 데이터베이스에 전송 되는 단일 쿼리가 검색 된 각 엔터티에 대 한 별도의 쿼리 보다 더 효율적 이기 때문에 즉시 로드는 최상의 성능을 제공 합니다.In general, if you know you need related data for every entity retrieved, eager loading offers the best performance, because a single query sent to the database is typically more efficient than separate queries for each entity retrieved. 반면, 엔터티 탐색 속성에 자주 액세스 하거나 작은 엔터티 집합에 대해서만 액세스 해야 하는 경우 즉시 로드는 필요한 것 보다 더 많은 데이터를 검색 하기 때문에 지연 로드 또는 명시적 로드가 더 효율적일 수 있습니다.On the other hand, if you need to access an entity's navigation properties only infrequently or only for a small set of the entities, lazy loading or explicit loading may be more efficient, because eager loading would retrieve more data than you need.

웹 응용 프로그램에서는 페이지를 렌더링 하는 개체 컨텍스트에 대 한 연결이 없는 브라우저에서 관련 데이터의 필요성에 영향을 주는 사용자 작업이 발생 하기 때문에 지연 로드는 상대적으로 작은 값일 수 있습니다.In a web application, lazy loading may be of relatively little value anyway, because user actions that affect the need for related data take place in the browser, which has no connection to the object context that rendered the page. 반면, 컨트롤의 데이터를 데이터 바인딩할 때 일반적으로 필요한 데이터를 알고 있으므로 일반적으로 각 시나리오의 적절 한 사항에 따라 즉시 로드 또는 지연 된 로드를 선택 하는 것이 좋습니다.On the other hand, when you databind a control, you typically know what data you need, and so it's generally best to choose eager loading or deferred loading based on what's appropriate in each scenario.

또한 개체 컨텍스트를 삭제 한 후에는 데이터 바인딩된 컨트롤이 엔터티 개체를 사용할 수 있습니다.In addition, a databound control might use an entity object after the object context is disposed. 이 경우 탐색 속성을 지연 로드 하려는 시도가 실패 합니다.In that case, an attempt to lazy-load a navigation property would fail. 표시 되는 오류 메시지는 다음과 같습니다. "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."The error message you receive is clear: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."

EntityDataSource 컨트롤은 기본적으로 지연 로드를 사용 하지 않도록 설정 합니다.The EntityDataSource control disables lazy loading by default. 현재 자습서에 사용 하는 ObjectDataSource 컨트롤 또는 페이지 코드에서 개체 컨텍스트에 액세스 하는 경우 여러 가지 방법으로 지연 로드를 기본적으로 사용 하지 않도록 설정할 수 있습니다.For the ObjectDataSource control that you're using for the current tutorial (or if you access the object context from page code), there are several ways you can make lazy loading disabled by default. 개체 컨텍스트를 인스턴스화할 때 사용 하지 않도록 설정할 수 있습니다.You can disable it when you instantiate an object context. 예를 들어 SchoolRepository 클래스의 생성자 메서드에 다음 줄을 추가할 수 있습니다.For example, you can add the following line to the constructor method of the SchoolRepository class:

context.ContextOptions.LazyLoadingEnabled = false;

Contoso 대학 응용 프로그램의 경우 컨텍스트를 인스턴스화할 때마다이 속성을 설정할 필요가 없도록 개체 컨텍스트를 지연 로드를 자동으로 사용 하지 않도록 설정 합니다.For the Contoso University application, you'll make the object context automatically disable lazy loading so that this property doesn't have to be set whenever a context is instantiated.

SchoolModel 데이터 모델을 열고 디자인 화면을 클릭 한 다음 속성 창에서 지연 로드 설정 속성을 False로 설정 합니다.Open the SchoolModel.edmx data model, click the design surface, and then in the properties pane set the Lazy Loading Enabled property to False. 데이터 모델을 저장 하 고 닫습니다.Save and close the data model.

Image04Image04

뷰 상태 관리Managing View State

업데이트 기능을 제공 하기 위해 페이지를 렌더링할 때 ASP.NET 웹 페이지에서 엔터티의 원래 속성 값을 저장 해야 합니다.In order to provide update functionality, an ASP.NET web page must store the original property values of an entity when a page is rendered. 포스트백 처리 중에 컨트롤은 엔터티의 원래 상태를 다시 만들고 변경 내용을 적용 하 고 SaveChanges 메서드를 호출 하기 전에 엔터티의 Attach 메서드를 호출할 수 있습니다.During postback processing the control can re-create the original state of the entity and call the entity's Attach method before applying changes and calling the SaveChanges method. 기본적으로 ASP.NET Web Forms 데이터 컨트롤은 뷰 상태를 사용 하 여 원래 값을 저장 합니다.By default, ASP.NET Web Forms data controls use view state to store the original values. 그러나 뷰 상태는 브라우저에서 보내고 받는 페이지의 크기를 크게 늘릴 수 있는 숨겨진 필드에 저장 되기 때문에 성능에 영향을 줄 수 있습니다.However, view state can affect performance, because it's stored in hidden fields that can substantially increase the size of the page that's sent to and from the browser.

뷰 상태 또는 세션 상태와 같은 대안을 관리 하는 기술은 Entity Framework에 고유 하지 않으므로이 자습서에서는이 항목에 대해 자세히 설명 하지 않습니다.Techniques for managing view state, or alternatives such as session state, aren't unique to the Entity Framework, so this tutorial doesn't go into this topic in detail. 자세한 내용은 자습서 끝에 있는 링크를 참조 하세요.For more information see the links at the end of the tutorial.

그러나 ASP.NET 버전 4는 모든 ASP.NET 응용 프로그램 개발자 Web Forms가 ViewStateMode 속성을 인식 해야 하는 뷰 상태를 사용 하는 새로운 방법을 제공 합니다.However, version 4 of ASP.NET provides a new way of working with view state that every ASP.NET developer of Web Forms applications should be aware of: the ViewStateMode property. 이 새로운 속성은 페이지나 컨트롤 수준에서 설정 될 수 있으며, 페이지에 대해 기본적으로 뷰 상태를 사용 하지 않도록 설정 하 고 필요한 컨트롤에 대해서만 사용 하도록 설정할 수 있습니다.This new property can be set at the page or control level, and it enables you to disable view state by default for a page and enable it only for controls that need it.

성능이 중요 한 응용 프로그램의 경우 페이지 수준에서 항상 보기 상태를 사용 하지 않도록 설정 하 고이를 필요로 하는 컨트롤에 대해서만 사용 하도록 설정 하는 것이 좋습니다.For applications where performance is critical, a good practice is to always disable view state at the page level and enable it only for controls that require it. Contoso 대학 페이지의 보기 상태 크기는이 방법으로 크게 감소 하지는 않지만 어떻게 작동 하는지 확인 하기 위해 강사 .aspx 페이지에 대해이 작업을 수행 합니다.The size of view state in the Contoso University pages wouldn't be substantially decreased by this method, but to see how it works, you'll do it for the Instructors.aspx page. 이 페이지에는 뷰 상태가 사용 하지 않도록 설정 된 Label 컨트롤을 포함 하 여 많은 컨트롤이 포함 되어 있습니다.That page contains many controls, including a Label control that has view state disabled. 이 페이지의 컨트롤은 실제로 보기 상태를 사용 하도록 설정 하지 않아도 됩니다.None of the controls on this page actually need to have view state enabled. GridView 컨트롤의 DataKeyNames 속성은 다시 게시 간에 유지 해야 하는 상태를 지정 하지만 이러한 값은 ViewStateMode 속성의 영향을 받지 않는 컨트롤 상태에 유지 됩니다.(The DataKeyNames property of the GridView control specifies state that must be maintained between postbacks, but these values are kept in control state, which isn't affected by the ViewStateMode property.)

현재 Page 지시문 및 Label 컨트롤 태그는 다음 예제와 비슷합니다.The Page directive and Label control markup currently resembles the following example:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors" %>
    ...
    <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label> 
    ...

다음과 같이 변경합니다.Make the following changes:

  • Page 지시문에 ViewStateMode="Disabled"를 추가 합니다.Add ViewStateMode="Disabled" to the Page directive.
  • Label 컨트롤에서 ViewStateMode="Disabled"를 제거 합니다.Remove ViewStateMode="Disabled" from the Label control.

이제 태그는 다음 예제와 유사 합니다.The markup now resembles the following example:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors" 
    ViewStateMode="Disabled" %>
    ...
    <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false"></asp:Label> 
    ...

이제 모든 컨트롤에서 뷰 상태를 사용할 수 없습니다.View state is now disabled for all controls. 나중에 뷰 상태를 사용 해야 하는 컨트롤을 추가 하는 경우 해당 컨트롤에 대 한 ViewStateMode="Enabled" 특성만 포함 하기만 하면 됩니다.If you later add a control that does need to use view state, all you need to do is include the ViewStateMode="Enabled" attribute for that control.

NoTracking Merge 옵션 사용Using The NoTracking Merge Option

개체 컨텍스트가 데이터베이스 행을 검색 하 고이를 나타내는 엔터티 개체를 만드는 경우 기본적으로 해당 개체 상태 관리자를 사용 하 여 해당 엔터티 개체도 추적 합니다.When an object context retrieves database rows and creates entity objects that represent them, by default it also tracks those entity objects using its object state manager. 이 추적 데이터는 캐시 역할을 하며 엔터티를 업데이트할 때 사용 됩니다.This tracking data acts as a cache and is used when you update an entity. 웹 응용 프로그램에는 일반적으로 수명이 짧은 개체 컨텍스트 인스턴스가 있기 때문에 쿼리는 추적 하지 않아도 되는 데이터를 반환 하는 경우가 많습니다. 이러한 데이터는이를 읽는 엔터티가 다시 사용 되거나 업데이트 되기 전에 삭제 됩니다.Because a web application typically has short-lived object context instances, queries often return data that doesn't need to be tracked, because the object context that reads them will be disposed before any of the entities it reads are used again or updated.

Entity Framework에서 병합 옵션을 설정 하 여 개체 컨텍스트에서 엔터티 개체를 추적할지 여부를 지정할 수 있습니다.In the Entity Framework, you can specify whether the object context tracks entity objects by setting a merge option. 개별 쿼리 또는 엔터티 집합에 대 한 병합 옵션을 설정할 수 있습니다.You can set the merge option for individual queries or for entity sets. 엔터티 집합에 대해이 옵션을 설정 하는 경우 해당 엔터티 집합에 대해 생성 된 모든 쿼리에 대해 기본 병합 옵션을 설정 하는 것입니다.If you set it for an entity set, that means that you're setting the default merge option for all queries that are created for that entity set.

Contoso 대학 응용 프로그램의 경우 리포지토리에 액세스 하는 엔터티 집합에는 추적이 필요 하지 않으므로 리포지토리 클래스에서 개체 컨텍스트를 인스턴스화할 때 이러한 엔터티 집합에 대 한 NoTracking 병합 옵션을 설정할 수 있습니다.For the Contoso University application, tracking isn't needed for any of the entity sets that you access from the repository, so you can set the merge option to NoTracking for those entity sets when you instantiate the object context in the repository class. 이 자습서에서는 병합 옵션을 설정 하는 것이 응용 프로그램의 성능에 크게 영향을 주지 않습니다.(Note that in this tutorial, setting the merge option won't have a noticeable effect on the application's performance. NoTracking 옵션은 특정 대량 볼륨 시나리오 에서만 예측 가능한 성능을 향상 시킬 수 있습니다.The NoTracking option is likely to make an observable performance improvement only in certain high-data-volume scenarios.)

DAL 폴더에서 SchoolRepository.cs 파일을 열고 리포지토리가 액세스 하는 엔터티 집합에 대 한 병합 옵션을 설정 하는 생성자 메서드를 추가 합니다.In the DAL folder, open the SchoolRepository.cs file and add a constructor method that sets the merge option for the entity sets that the repository accesses:

public SchoolRepository()
{
    context.Departments.MergeOption = MergeOption.NoTracking;
    context.InstructorNames.MergeOption = MergeOption.NoTracking;
    context.OfficeAssignments.MergeOption = MergeOption.NoTracking;
}

LINQ 쿼리 미리 컴파일Pre-Compiling LINQ Queries

Entity Framework에서 지정 된 ObjectContext 인스턴스 수명 내에 Entity SQL 쿼리를 처음 실행 하면 쿼리를 컴파일하는 데 약간의 시간이 걸립니다.The first time that the Entity Framework executes an Entity SQL query within the life of a given ObjectContext instance, it takes some time to compile the query. 컴파일의 결과가 캐시 됩니다. 즉, 이후의 쿼리 실행이 훨씬 빨라집니다.The result of compilation is cached, which means that subsequent executions of the query are much quicker. LINQ 쿼리는 쿼리가 실행 될 때마다 쿼리를 컴파일하는 데 필요한 일부 작업을 수행 한다는 점을 제외 하 고 비슷한 패턴을 따릅니다.LINQ queries follow a similar pattern, except that some of the work required to compile the query is done every time the query is executed. 즉, LINQ 쿼리의 경우에는 기본적으로 컴파일 결과가 모두 캐시 되지 않습니다.In other words, for LINQ queries, by default not all of the results of compilation are cached.

개체 컨텍스트 수명 동안 반복적으로 실행 해야 하는 LINQ 쿼리가 있는 경우 LINQ 쿼리가 처음 실행 될 때 컴파일 결과가 모두 캐시 되도록 하는 코드를 작성할 수 있습니다.If you have a LINQ query that you expect to run repeatedly in the life of an object context, you can write code that causes all of the results of compilation to be cached the first time the LINQ query is run.

이에 대 한 설명으로, SchoolRepository 클래스의 두 가지 Get 메서드에 대해이 작업을 수행 합니다. 그 중 하나는 매개 변수 (GetInstructorNames 메서드)를 사용 하지 않고 다른 하나는 매개 변수 (GetDepartmentsByAdministrator 메서드)를 필요로 합니다.As an illustration, you'll do this for two Get methods in the SchoolRepository class, one of which doesn't take any parameters (the GetInstructorNames method), and one that does require a parameter (the GetDepartmentsByAdministrator method). 이러한 메서드는 이제는 LINQ 쿼리가 아니기 때문에 실제로 컴파일할 필요가 없습니다.These methods as they stand now actually don't need to be compiled because they aren't LINQ queries:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return new ObjectQuery<Department>("SELECT VALUE d FROM Departments as d", context, MergeOption.NoTracking).Include("Person").Where(d => d.Administrator == administrator).ToList();
}

그러나 컴파일된 쿼리를 사용해 볼 수 있도록 다음 LINQ 쿼리로 작성 된 것 처럼 계속 진행 합니다.However, so that you can try out compiled queries, you'll proceed as if these had been written as the following LINQ queries:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return (from i in context.InstructorNames orderby i.FullName select i).ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    context.Departments.MergeOption = MergeOption.NoTracking;
    return (from d in context.Departments where d.Administrator == administrator select d).ToList();
}

이러한 메서드의 코드를 위에 표시 된 내용으로 변경 하 고 응용 프로그램을 실행 하 여 계속 하기 전에 작동 하는지 확인할 수 있습니다.You could change the code in these methods to what's shown above and run the application to verify that it works before continuing. 그러나 다음 지침에서는 미리 컴파일된 버전의를 직접 만듭니다.But the following instructions jump right into creating pre-compiled versions of them.

DAL 폴더에 클래스 파일을 만들고 이름을 SchoolEntities.cs로 바꾸고 기존 코드를 다음 코드로 바꿉니다.Create a class file in the DAL folder, name it SchoolEntities.cs, and replace the existing code with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Objects;

namespace ContosoUniversity.DAL
{
    public partial class SchoolEntities
    {
        private static readonly Func<SchoolEntities, IQueryable<InstructorName>> compiledInstructorNamesQuery =
            CompiledQuery.Compile((SchoolEntities context) => from i in context.InstructorNames orderby i.FullName select i);

        public IEnumerable<InstructorName> CompiledInstructorNamesQuery()
        {
            return compiledInstructorNamesQuery(this).ToList();
        }

        private static readonly Func<SchoolEntities, Int32, IQueryable<Department>> compiledDepartmentsByAdministratorQuery =
            CompiledQuery.Compile((SchoolEntities context, Int32 administrator) => from d in context.Departments.Include("Person") where d.Administrator == administrator select d);

        public IEnumerable<Department> CompiledDepartmentsByAdministratorQuery(Int32 administrator)
        {
            return compiledDepartmentsByAdministratorQuery(this, administrator).ToList();
        }
    }
}

이 코드는 자동으로 생성 된 개체 컨텍스트 클래스를 확장 하는 partial 클래스를 만듭니다.This code creates a partial class that extends the automatically generated object context class. Partial 클래스는 CompiledQuery 클래스의 Compile 메서드를 사용 하 여 컴파일된 두 LINQ 쿼리를 포함 합니다.The partial class includes two compiled LINQ queries using the Compile method of the CompiledQuery class. 또한 쿼리를 호출 하는 데 사용할 수 있는 메서드를 만듭니다.It also creates methods that you can use to call the queries. 이 파일을 저장 하 고 닫습니다.Save and close this file.

그런 다음 SchoolRepository.cs에서 컴파일된 쿼리를 호출 하도록 리포지토리 클래스의 기존 GetInstructorNamesGetDepartmentsByAdministrator 메서드를 변경 합니다.Next, in SchoolRepository.cs, change the existing GetInstructorNames and GetDepartmentsByAdministrator methods in the repository class so that they call the compiled queries:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.CompiledInstructorNamesQuery();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return context.CompiledDepartmentsByAdministratorQuery(administrator);
}

학과 페이지 를 실행 하 여 이전과 동일한 방식으로 작동 하는지 확인 합니다.Run the Departments.aspx page to verify that it works as it did before. GetInstructorNames 메서드는 관리자 드롭다운 목록을 채우기 위해 호출 되며, 업데이트 를 클릭 하 여 두 개 이상의 부서에 대 한 관리자가 없는지 확인 하기 위해 GetDepartmentsByAdministrator 메서드가 호출 됩니다.The GetInstructorNames method is called in order to populate the administrator drop-down list, and the GetDepartmentsByAdministrator method is called when you click Update in order to verify that no instructor is an administrator of more than one department.

Image03Image03

이 작업을 수행 하는 방법을 확인 하기 위해 Contoso 대학 응용 프로그램의 쿼리는 미리 컴파일되어 성능이 크게 성능이 향상 되지 않습니다.You've pre-compiled queries in the Contoso University application only to see how to do it, not because it would measurably improve performance. LINQ 쿼리를 미리 컴파일하면 코드에 복잡성 수준이 추가 되므로 실제로 응용 프로그램에서 성능 병목 현상을 나타내는 쿼리에만이 작업을 수행 해야 합니다.Pre-compiling LINQ queries does add a level of complexity to your code, so make sure you do it only for queries that actually represent performance bottlenecks in your application.

데이터베이스로 전송 된 쿼리 검사Examining Queries Sent to the Database

성능 문제를 조사할 때 Entity Framework는 데이터베이스에 전송 하는 정확한 SQL 명령을 파악 하는 것이 도움이 될 수 있습니다.When you're investigating performance issues, sometimes it's helpful to know the exact SQL commands that the Entity Framework is sending to the database. IQueryable 개체로 작업 하는 경우이 작업을 수행 하는 한 가지 방법은 ToTraceString 메서드를 사용 하는 것입니다.If you're working with an IQueryable object, one way to do this is to use the ToTraceString method.

SchoolRepository.cs에서 다음 예제와 일치 하도록 GetDepartmentsByName 메서드의 코드를 변경 합니다.In SchoolRepository.cs, change the code in the GetDepartmentsByName method to match the following example:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    ...
    var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Include("Person").Include("Courses").Where(d => d.Name.Contains(nameSearchString));
    string commandText = ((ObjectQuery)departments).ToTraceString();
    return departments.ToList();
}

이전 줄의 끝에 있는 Where 메서드가 IQueryable 개체를 만들기 때문에 departments 변수를 ObjectQuery 형식으로 캐스팅 해야 합니다. Where 메서드가 없으면 캐스트가 필요 하지 않습니다.The departments variable must be cast to an ObjectQuery type only because the Where method at the end of the preceding line creates an IQueryable object; without the Where method, the cast would not be necessary.

return 줄에 중단점을 설정한 다음 디버거의 .aspx 페이지를 실행 합니다.Set a breakpoint on the return line, and then run the Departments.aspx page in the debugger. 중단점에 도달 하면 지역 창에서 commandText 변수를 검사 하 고 텍스트 시각화 도우미 ( 열의 돋보기)를 사용 하 여 텍스트 시각화 도우미 창에 해당 값을 표시 합니다.When you hit the breakpoint, examine the commandText variable in the Locals window and use the text visualizer (the magnifying glass in the Value column) to display its value in the Text Visualizer window. 이 코드의 결과로 생성 되는 전체 SQL 명령을 볼 수 있습니다.You can see the entire SQL command that results from this code:

Image08Image08

또는 Visual Studio Ultimate의 IntelliTrace 기능을 통해 코드를 변경 하거나 중단점을 설정 하지 않아도 되는 Entity Framework에서 생성 된 SQL 명령을 볼 수 있습니다.As an alternative, the IntelliTrace feature in Visual Studio Ultimate provides a way to view SQL commands generated by the Entity Framework that doesn't require you to change your code or even set a breakpoint.

Note

Visual Studio Ultimate 경우에만 다음 절차를 수행할 수 있습니다.You can perform the following procedures only if you have Visual Studio Ultimate.

GetDepartmentsByName 메서드에서 원래 코드를 복원한 다음 디버거의 .aspx 페이지를 실행 합니다.Restore the original code in the GetDepartmentsByName method, and then run the Departments.aspx page in the debugger.

Visual Studio에서 디버그 메뉴, Intellitrace, intellitrace 이벤트를 차례로 선택 합니다.In Visual Studio, select the Debug menu, then IntelliTrace, and then IntelliTrace Events.

Image11Image11

IntelliTrace 창에서 모두 중단을 클릭 합니다.In the IntelliTrace window, click Break All.

Image12Image12

IntelliTrace 창에는 최근 이벤트의 목록이 표시 됩니다.The IntelliTrace window displays a list of recent events:

Image09Image09

ADO.NET 줄을 클릭 합니다.Click the ADO.NET line. 확장 하 여 명령 텍스트를 표시 합니다.It expands to show you the command text:

Image10Image10

전체 명령 텍스트 문자열을 로컬 창에서 클립보드에 복사할 수 있습니다.You can copy the entire command text string to the clipboard from the Locals window.

간단한 School 데이터베이스 보다 많은 테이블, 관계 및 열이 포함 된 데이터베이스를 사용 하 고 있다고 가정 합니다.Suppose you were working with a database with more tables, relationships, and columns than the simple School database. 여러 Join 절을 포함 하는 단일 Select 문에 필요한 모든 정보를 수집 하는 쿼리가 너무 복잡 하 여 효율적으로 작업할 수 없는 경우가 있습니다.You might find that a query that gathers all the information you need in a single Select statement containing multiple Join clauses becomes too complex to work efficiently. 이 경우 즉시 로드에서 명시적 로드로 전환 하 여 쿼리를 단순화할 수 있습니다.In that case you can switch from eager loading to explicit loading to simplify the query.

예를 들어 SchoolRepository.csGetDepartmentsByName 메서드에서 코드를 변경해 봅니다.For example, try changing the code in the GetDepartmentsByName method in SchoolRepository.cs. 현재이 메서드는 Person에 대 한 Include 메서드와 탐색 속성을 Courses 하는 개체 쿼리가 있습니다.Currently in that method you have an object query that has Include methods for the Person and Courses navigation properties. 다음 예제와 같이 return 문을 명시적 로드를 수행 하는 코드로 바꿉니다.Replace the return statement with code that performs explicit loading, as shown in the following example:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    ...
    var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Where(d => d.Name.Contains(nameSearchString)).ToList();
    foreach (Department d in departments)
    {
        d.Courses.Load();
        d.PersonReference.Load();
    }
    return departments;
}

디버거에서 학과 페이지를 실행 하 고 이전과 같은 방법으로 IntelliTrace 창을 다시 확인 합니다.Run the Departments.aspx page in the debugger and check the IntelliTrace window again as you did before. 이전에는 단일 쿼리를 수행 하는 위치에 대 한 자세한 시퀀스가 표시 됩니다.Now, where there was a single query before, you see a long sequence of them.

Image13Image13

첫 번째 ADO.NET 줄을 클릭 하 여 앞에서 본 복잡 한 쿼리에 대 한 변경 내용을 확인 합니다.Click the first ADO.NET line to see what has happened to the complex query you viewed earlier.

Image14Image14

부서에서 쿼리는 Join 절을 사용 하지 않는 간단한 Select 쿼리가 되었지만 그 다음에는 원래 쿼리에서 반환 된 각 부서에 대해 두 개의 쿼리 집합을 사용 하 여 관련 과정 및 관리자를 검색 하는 별도의 쿼리가 적용 됩니다.The query from Departments has become a simple Select query with no Join clause, but it's followed by separate queries that retrieve related courses and an administrator, using a set of two queries for each department returned by the original query.

Note

지연 로드를 사용 하는 경우 여기에 표시 되는 패턴은 동일한 쿼리를 여러 번 반복 하 여 지연 로드로 인해 발생할 수 있습니다.If you leave lazy loading enabled, the pattern you see here, with the same query repeated many times, might result from lazy loading. 일반적으로 사용 하지 않으려는 패턴은 기본 테이블의 모든 행에 대 한 지연 로드 관련 데이터입니다.A pattern that you typically want to avoid is lazy-loading related data for every row of the primary table. 단일 조인 쿼리가 너무 복잡 한 것을 확인 하지 않은 경우에는 일반적으로 즉시 로드를 사용 하도록 기본 쿼리를 변경 하 여 성능을 향상 시킬 수 있습니다.Unless you've verified that a single join query is too complex to be efficient, you'd typically be able to improve performance in such cases by changing the primary query to use eager loading.

미리 생성 된 뷰Pre-Generating Views

새 응용 프로그램 도메인에서 ObjectContext 개체를 처음 만들 때 Entity Framework는 데이터베이스에 액세스 하는 데 사용 하는 클래스 집합을 생성 합니다.When an ObjectContext object is first created in a new application domain, the Entity Framework generates a set of classes that it uses to access the database. 이러한 클래스를 라고 하며, 데이터 모델이 매우 큰 경우 이러한 뷰를 생성 하면 새 응용 프로그램 도메인이 초기화 된 후에 페이지에 대 한 첫 번째 요청에 대 한 웹 사이트의 응답이 지연 될 수 있습니다.These classes are called views, and if you have a very large data model, generating these views can delay the web site's response to the first request for a page after a new application domain is initialized. 런타임 대신 컴파일 시간에 뷰를 만들어이 첫 번째 요청 지연을 줄일 수 있습니다.You can reduce this first-request delay by creating the views at compile time rather than at run time.

Note

응용 프로그램에 매우 큰 데이터 모델이 없거나 큰 데이터 모델이 있지만 IIS를 재활용 하 고 첫 번째 페이지 요청에만 영향을 주는 성능 문제를 걱정 하지 않는 경우이 섹션을 건너뛸 수 있습니다.If your application doesn't have an extremely large data model, or if it does have a large data model but you aren't concerned about a performance problem that affects only the very first page request after IIS is recycled, you can skip this section. 뷰가 응용 프로그램 도메인에 캐시 되기 때문에 ObjectContext 개체를 인스턴스화할 때마다 뷰 만들기가 발생 하지 않습니다.View creation doesn't happen every time you instantiate an ObjectContext object, because the views are cached in the application domain. 따라서 IIS에서 응용 프로그램을 자주 재활용 하지 않는 한 페이지 요청 수가 너무 적으면 미리 생성 된 뷰에서 이점을 누릴 수 있습니다.Therefore, unless you're frequently recycling your application in IIS, very few page requests would benefit from pre-generated views.

Edmgen.exe 명령줄 도구를 사용 하거나 텍스트 템플릿 변환 도구 키트 (T4) 템플릿을 사용 하 여 뷰를 미리 생성할 수 있습니다.You can pre-generate views using the EdmGen.exe command-line tool or by using a Text Template Transformation Toolkit (T4) template. 이 자습서에서는 T4 템플릿을 사용 합니다.In this tutorial you'll use a T4 template.

DAL 폴더에서 텍스트 템플릿 템플릿을 사용 하 여 파일을 추가 하 고 ( 설치 된 템플릿 목록의 일반 노드 아래에 있는) 이름을 SchoolModel.Views.tt로 표시 합니다.In the DAL folder, add a file using the Text Template template (it's under the General node in the Installed Templates list), and name it SchoolModel.Views.tt. 파일의 기존 코드를 다음 코드로 바꿉니다.Replace the existing code in the file with the following code:

<#
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.

THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/
#>

<#
    //
    // TITLE: T4 template to generate views for an EDMX file in a C# project
    //
    // DESCRIPTION:
    // This is a T4 template to generate views in C# for an EDMX file in C# projects.
    // The generated views are automatically compiled into the project's output assembly.
    //
    // This template follows a simple file naming convention to determine the EDMX file to process:
    // - It assumes that [edmx-file-name].Views.tt will process and generate views for [edmx-file-name].EDMX
    // - The views are generated in the code behind file [edmx-file-name].Views.cs
    //
    // USAGE:
    // Do the following to generate views for an EDMX file (e.g. Model1.edmx) in a C# project
    // 1. In Solution Explorer, right-click the project node and choose "Add...Existing...Item" from the context menu
    // 2. Browse to and choose this .tt file to include it in the project 
    // 3. Ensure this .tt file is in the same directory as the EDMX file to process 
    // 4. In Solution Explorer, rename this .tt file to the form [edmx-file-name].Views.tt (e.g. Model1.Views.tt)
    // 5. In Solution Explorer, right-click Model1.Views.tt and choose "Run Custom Tool" to generate the views
    // 6. The views are generated in the code behind file Model1.Views.cs
    //
    // TIPS:
    // If you have multiple EDMX files in your project then make as many copies of this .tt file and rename appropriately
    // to pair each with each EDMX file.
    //
    // To generate views for all EDMX files in the solution, click the "Transform All Templates" button in the Solution Explorer toolbar
    // (its the rightmost button in the toolbar) 
    //
#>
<#
    //
    // T4 template code follows
    //
#>
<#@ template language="C#" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<# 
    // Find EDMX file to process: Model1.Views.tt generates views for Model1.EDMX
    string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";
    string edmxFilePath = Path.Combine(Path.GetDirectoryName(this.Host.TemplateFile), edmxFileName);
    if (File.Exists(edmxFilePath))
    {
        // Call helper class to generate pre-compiled views and write to output
        this.WriteLine(GenerateViews(edmxFilePath));
    }
    else
    {
        this.Error(String.Format("No views were generated. Cannot find file {0}. Ensure the project has an EDMX file and the file name of the .tt file is of the form [edmx-file-name].Views.tt", edmxFilePath));
    }
    
    // All done!
#>

<#+
    private String GenerateViews(string edmxFilePath)
    {
        MetadataLoader loader = new MetadataLoader(this);
        MetadataWorkspace workspace;
        if(!loader.TryLoadAllMetadata(edmxFilePath, out workspace))
        {
            this.Error("Error in the metadata");
            return String.Empty;
        }
            
        String generatedViews = String.Empty;
        try
        {
            using (StreamWriter writer = new StreamWriter(new MemoryStream()))
            {
                StorageMappingItemCollection mappingItems = (StorageMappingItemCollection)workspace.GetItemCollection(DataSpace.CSSpace);

                // Initialize the view generator to generate views in C#
                EntityViewGenerator viewGenerator = new EntityViewGenerator();
                viewGenerator.LanguageOption = LanguageOption.GenerateCSharpCode;
                IList<EdmSchemaError> errors = viewGenerator.GenerateViews(mappingItems, writer);

                foreach (EdmSchemaError e in errors)
                {
                    // log error
                    this.Error(e.Message);
                }

                MemoryStream memStream = writer.BaseStream as MemoryStream;
                generatedViews = Encoding.UTF8.GetString(memStream.ToArray());
            }
        }
        catch (Exception ex)
        {
            // log error
            this.Error(ex.ToString());
        }

        return generatedViews;
    }
#>

이 코드는 템플릿과 동일한 폴더에 있으며 템플릿 파일과 이름이 같은 .edmx 파일에 대 한 뷰를 생성 합니다.This code generates views for an .edmx file that's located in the same folder as the template and that has the same name as the template file. 예를 들어 템플릿 파일의 이름이 SchoolModel.Views.tt인 경우 SchoolModel라는 데이터 모델 파일이 검색 됩니다.For example, if your template file is named SchoolModel.Views.tt, it will look for a data model file named SchoolModel.edmx.

파일을 저장 한 다음 솔루션 탐색기 파일을 마우스 오른쪽 단추로 클릭 하 고 사용자 지정 도구 실행을 선택 합니다.Save the file, then right-click the file in Solution Explorer and select Run Custom Tool.

Image02Image02

Visual Studio는 템플릿을 기반으로 하는 SchoolModel.Views.cs 라는 뷰를 만드는 코드 파일을 생성 합니다.Visual Studio generates a code file that creates the views, which is named SchoolModel.Views.cs based on the template. 템플릿 파일을 저장 하는 즉시 사용자 지정 도구 실행을 선택 하기 전에 코드 파일이 생성 되었다는 것을 알 수 있습니다.(You might have noticed that the code file is generated even before you select Run Custom Tool, as soon as you save the template file.)

Image01Image01

이제 응용 프로그램을 실행 하 고 이전과 같은 방식으로 작동 하는지 확인할 수 있습니다.You can now run the application and verify that it works as it did before.

미리 생성 된 뷰에 대 한 자세한 내용은 다음 리소스를 참조 하세요.For more information about pre-generated views, see the following resources:

이렇게 하면 Entity Framework를 사용 하는 ASP.NET 웹 응용 프로그램의 성능 향상에 대 한 소개를 완료 합니다.This completes the introduction to improving performance in an ASP.NET web application that uses the Entity Framework. 자세한 내용은 다음 리소스를 참조하세요.For more information, see the following resources:

다음 자습서에서는 버전 4의 새로운 기능에 Entity Framework 대 한 몇 가지 중요 한 향상 된 기능을 검토 합니다.The next tutorial reviews some of the important enhancements to the Entity Framework that are new in version 4.