Entity Framework에서 구조적으로 유사한 쿼리를 여러 번 실행하는 애플리케이션이 있는 경우 쿼리를 한 번 컴파일하고 다른 매개 변수로 여러 번 실행하여 성능을 높일 수 있습니다. 예를 들어 애플리케이션은 특정 도시의 모든 고객을 검색해야 할 수 있습니다. 사용자가 런타임에 구/군/시를 폼으로 지정합니다. LINQ to Entities는 이 목적을 위해 컴파일된 쿼리 사용을 지원합니다.
.NET Framework 4.5부터 LINQ 쿼리는 자동으로 캐시됩니다. 그러나 컴파일된 LINQ 쿼리를 사용하여 이후 실행에서 이 비용을 줄일 수 있으며 컴파일된 쿼리는 자동으로 캐시되는 LINQ 쿼리보다 더 효율적일 수 있습니다. 메모리 내 컬렉션에 Enumerable.Contains 연산자를 적용하는 LINQ to Entities 쿼리는 자동으로 캐시되지 않습니다. 또한 컴파일된 LINQ 쿼리에서 메모리 내 컬렉션을 매개 변수화할 수 없습니다.
CompiledQuery 클래스는 다시 사용할 쿼리의 컴파일 및 캐싱을 제공합니다. 개념적으로 이 클래스에는 여러 오버로드가 있는 CompiledQueryCompile 메서드가 포함되어 있습니다.
Compile 메서드를 호출하여 컴파일된 쿼리를 나타내는 새 대리자를 만듭니다.
Compile 및 매개 변수 값과 함께 제공되는 ObjectContext 메서드는 일부 결과(예: IQueryable<T> 인스턴스)를 생성하는 대리자를 반환합니다. 쿼리는 첫 번째 실행 중에 한 번만 컴파일됩니다. 컴파일 시 쿼리에 대해 설정된 병합 옵션은 나중에 변경할 수 없습니다. 쿼리가 컴파일되면 기본 형식의 매개 변수만 제공할 수 있지만 생성된 SQL을 변경하는 쿼리 부분을 바꿀 수는 없습니다. 자세한 내용은 EF 병합 옵션 및 컴파일된 쿼리참조하세요.
CompiledQuery의 Compile 메서드가 컴파일하는 LINQ to Entities 쿼리 식은 Func와 같은 제네릭 Func<T1,T2,T3,T4,TResult> 대리자로 표현됩니다. 쿼리 식은 최대 ObjectContext 매개 변수, 반환 매개 변수 및 16개의 쿼리 매개 변수를 캡슐화할 수 있습니다. 16개 이상의 쿼리 매개 변수가 필요한 경우 속성이 쿼리 매개 변수를 나타내는 구조를 만들 수 있습니다. 그런 다음 속성을 설정한 후 쿼리 식의 구조에 대한 속성을 사용할 수 있습니다.
예제 1
다음 예제에서는 Decimal 입력 매개 변수를 수락하고 총 기한이 $200.00보다 크거나 같은 주문 목록을 반환하는 쿼리를 컴파일한 후 호출합니다.
static readonly Func<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>> s_compiledQuery2 =
CompiledQuery.Compile<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>>(
(ctx, total) => from order in ctx.SalesOrderHeaders
where order.TotalDue >= total
select order);
static void CompiledQuery2()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
Decimal totalDue = 200.00M;
IQueryable<SalesOrderHeader> orders = s_compiledQuery2.Invoke(context, totalDue);
foreach (SalesOrderHeader order in orders)
{
Console.WriteLine($"ID: {order.SalesOrderID} Order date: {order.OrderDate} Total due: {order.TotalDue}");
}
}
}
ReadOnly s_compQuery2 As Func(Of AdventureWorksEntities, Decimal, IQueryable(Of SalesOrderHeader)) = _
CompiledQuery.Compile(Of AdventureWorksEntities, Decimal, IQueryable(Of SalesOrderHeader))( _
Function(ctx As AdventureWorksEntities, total As Decimal) _
From order In ctx.SalesOrderHeaders _
Where (order.TotalDue >= total) _
Select order)
Sub CompiledQuery2()
Using context As New AdventureWorksEntities()
Dim totalDue As Decimal = 200.0
Dim orders As IQueryable(Of SalesOrderHeader) = s_compQuery2.Invoke(context, totalDue)
For Each order In orders
Console.WriteLine("ID: {0} Order date: {1} Total due: {2}", _
order.SalesOrderID, _
order.OrderDate, _
order.TotalDue)
Next
End Using
End Sub
예제 2
다음 예제에서는 ObjectQuery<T> 인스턴스를 반환하는 쿼리를 컴파일한 다음 호출합니다.
static readonly Func<AdventureWorksEntities, ObjectQuery<SalesOrderHeader>> s_compiledQuery1 =
CompiledQuery.Compile<AdventureWorksEntities, ObjectQuery<SalesOrderHeader>>(
ctx => ctx.SalesOrderHeaders);
static void CompiledQuery1_MQ()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<SalesOrderHeader> orders = s_compiledQuery1.Invoke(context);
foreach (SalesOrderHeader order in orders)
Console.WriteLine(order.SalesOrderID);
}
}
ReadOnly s_compQuery1 As Func(Of AdventureWorksEntities, ObjectQuery(Of SalesOrderHeader)) = _
CompiledQuery.Compile(Of AdventureWorksEntities, ObjectQuery(Of SalesOrderHeader))( _
Function(ctx) ctx.SalesOrderHeaders)
Sub CompiledQuery1_MQ()
Using context As New AdventureWorksEntities()
Dim orders As ObjectQuery(Of SalesOrderHeader) = s_compQuery1.Invoke(context)
For Each order In orders
Console.WriteLine(order.SalesOrderID)
Next
End Using
End Sub
예제 3
다음 예제에서는 제품 목록 가격의 평균을 Decimal 값으로 반환하는 쿼리를 컴파일한 다음 호출합니다.
static readonly Func<AdventureWorksEntities, Decimal> s_compiledQuery3MQ = CompiledQuery.Compile<AdventureWorksEntities, Decimal>(
ctx => ctx.Products.Average(product => product.ListPrice));
static void CompiledQuery3_MQ()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
Decimal averageProductPrice = s_compiledQuery3MQ.Invoke(context);
Console.WriteLine($"The average of the product list prices is $: {averageProductPrice}");
}
}
Using context As New AdventureWorksEntities()
Dim compQuery = CompiledQuery.Compile(Of AdventureWorksEntities, Decimal)( _
Function(ctx) ctx.Products.Average(Function(Product) Product.ListPrice))
Dim averageProductPrice As Decimal = compQuery.Invoke(context)
Console.WriteLine("The average of the product list prices is $: {0}", averageProductPrice)
End Using
예제 4
다음 예제에서는 String 입력 매개 변수를 수락하는 쿼리를 컴파일한 다음, 전자 메일 주소가 지정된 문자열로 시작하는 Contact 반환합니다.
static readonly Func<AdventureWorksEntities, string, Contact> s_compiledQuery4MQ =
CompiledQuery.Compile<AdventureWorksEntities, string, Contact>(
(ctx, name) => ctx.Contacts.First(contact => contact.EmailAddress.StartsWith(name)));
static void CompiledQuery4_MQ()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
string contactName = "caroline";
Contact foundContact = s_compiledQuery4MQ.Invoke(context, contactName);
Console.WriteLine($"An email address starting with 'caroline': {foundContact.EmailAddress}");
}
}
Using context As New AdventureWorksEntities()
Dim compQuery = CompiledQuery.Compile(Of AdventureWorksEntities, String, Contact)( _
Function(ctx, name) ctx.Contacts.First(Function(contact) contact.EmailAddress.StartsWith(name)))
Dim contactName As String = "caroline"
Dim foundContact As Contact = compQuery.Invoke(context, contactName)
Console.WriteLine("An email address starting with 'caroline': {0}", _
foundContact.EmailAddress)
End Using
예제 5
다음 예제에서는 입력 매개 변수를 DateTime과 Decimal로 받는 쿼리를 컴파일한 다음, 주문 날짜가 2003년 3월 8일보다 늦고 총 금액이 $300.00 미만인 주문의 목록을 반환합니다.
static readonly Func<AdventureWorksEntities, DateTime, Decimal, IQueryable<SalesOrderHeader>> s_compiledQuery5 =
CompiledQuery.Compile<AdventureWorksEntities, DateTime, Decimal, IQueryable<SalesOrderHeader>>(
(ctx, orderDate, totalDue) => from product in ctx.SalesOrderHeaders
where product.OrderDate > orderDate
&& product.TotalDue < totalDue
orderby product.OrderDate
select product);
static void CompiledQuery5()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
DateTime date = new DateTime(2003, 3, 8);
Decimal amountDue = 300.00M;
IQueryable<SalesOrderHeader> orders = s_compiledQuery5.Invoke(context, date, amountDue);
foreach (SalesOrderHeader order in orders)
{
Console.WriteLine($"ID: {order.SalesOrderID} Order date: {order.OrderDate} Total due: {order.TotalDue}");
}
}
}
ReadOnly s_compQuery5 = _
CompiledQuery.Compile(Of AdventureWorksEntities, DateTime, Decimal, IQueryable(Of SalesOrderHeader))( _
Function(ctx, orderDate, totalDue) From product In ctx.SalesOrderHeaders _
Where product.OrderDate > orderDate _
And product.TotalDue < totalDue _
Order By product.OrderDate _
Select product)
Sub CompiledQuery5()
Using context As New AdventureWorksEntities()
Dim orderedAfterDate As DateTime = New DateTime(2003, 3, 8)
Dim amountDue As Decimal = 300.0
Dim orders As IQueryable(Of SalesOrderHeader) = _
s_compQuery5.Invoke(context, orderedAfterDate, amountDue)
For Each order In orders
Console.WriteLine("ID: {0} Order date: {1} Total due: {2}", _
order.SalesOrderID, order.OrderDate, order.TotalDue)
Next
End Using
End Sub
예제 6
다음 예제에서는 DateTime 입력 매개 변수를 수락하고 주문 날짜가 2004년 3월 8일 이후인 순서 시퀀스를 반환하는 쿼리를 컴파일한 다음 호출합니다. 이 쿼리는 무명 형식의 시퀀스로 주문 정보를 반환합니다. 익명 형식은 컴파일러에서 유추되므로 CompiledQueryCompile 메서드에서 형식 매개 변수를 지정할 수 없으며 형식은 쿼리 자체에 정의됩니다.
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
var compiledQuery = CompiledQuery.Compile((AdventureWorksEntities ctx, DateTime orderDate) =>
from order in ctx.SalesOrderHeaders
where order.OrderDate > orderDate
select new {order.OrderDate, order.SalesOrderID, order.TotalDue});
DateTime date = new DateTime(2004, 3, 8);
var results = compiledQuery.Invoke(context, date);
foreach (var order in results)
{
Console.WriteLine($"ID: {order.SalesOrderID} Order date: {order.OrderDate} Total due: {order.TotalDue}");
}
}
Using context As New AdventureWorksEntities()
Dim compQuery = CompiledQuery.Compile( _
Function(ctx As AdventureWorksEntities, orderDate As DateTime) _
From order In ctx.SalesOrderHeaders _
Where order.OrderDate > orderDate _
Select New With {order.OrderDate, order.SalesOrderID, order.TotalDue})
Dim orderedAfterDate As DateTime = New DateTime(2004, 3, 8)
Dim orders = compQuery.Invoke(context, orderedAfterDate)
For Each order In orders
Console.WriteLine("ID: {0} Order date: {1} Total due: {2}", _
order.SalesOrderID, order.OrderDate, order.TotalDue)
Next
End Using
예제 7
다음 예제에서는 사용자 정의 구조 입력 매개 변수를 수락하고 순서 시퀀스를 반환하는 쿼리를 컴파일한 다음 호출합니다. 이 구조는 시작 날짜, 종료 날짜 및 총 기한 쿼리 매개 변수를 정의하며, 쿼리는 2003년 3월 3일부터 3월 8일 사이에 배송된 주문을 반환하며 총 기한은 $700.00보다 큽니다.
static Func<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>> s_compiledQuery =
CompiledQuery.Compile<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>>(
(ctx, myparams) => from sale in ctx.SalesOrderHeaders
where sale.ShipDate > myparams.startDate && sale.ShipDate < myparams.endDate
&& sale.TotalDue > myparams.totalDue
select sale);
static void CompiledQuery7()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
MyParams myParams = new MyParams();
myParams.startDate = new DateTime(2003, 3, 3);
myParams.endDate = new DateTime(2003, 3, 8);
myParams.totalDue = 700.00M;
IQueryable<SalesOrderHeader> sales = s_compiledQuery.Invoke(context, myParams);
foreach (SalesOrderHeader sale in sales)
{
Console.WriteLine($"ID: {sale.SalesOrderID}");
Console.WriteLine($"Ship date: {sale.ShipDate}");
Console.WriteLine($"Total due: {sale.TotalDue}");
}
}
}
ReadOnly s_compQuery = CompiledQuery.Compile(Of AdventureWorksEntities, MyParams, IQueryable(Of SalesOrderHeader))( _
Function(ctx, mySearchParams) _
From sale In ctx.SalesOrderHeaders _
Where sale.ShipDate > mySearchParams.startDate _
And sale.ShipDate < mySearchParams.endDate _
And sale.TotalDue > mySearchParams.totalDue _
Select sale)
Sub CompiledQuery7()
Using context As New AdventureWorksEntities()
Dim myParams As MyParams = New MyParams()
myParams.startDate = New DateTime(2003, 3, 3)
myParams.endDate = New DateTime(2003, 3, 8)
myParams.totalDue = 700.0
Dim sales = s_compQuery.Invoke(context, myParams)
For Each sale In sales
Console.WriteLine("ID: {0}", sale.SalesOrderID)
Console.WriteLine("Ship date: {0}", sale.ShipDate)
Console.WriteLine("Total due: {0}", sale.TotalDue)
Next
End Using
End Sub
쿼리 매개 변수를 정의하는 구조체:
struct MyParams
{
public DateTime startDate;
public DateTime endDate;
public decimal totalDue;
}
Public Structure MyParams
Public startDate As DateTime
Public endDate As DateTime
Public totalDue As Decimal
End Structure
참고하십시오
- ADO.NET Entity Framework
- LINQ to Entities
- EF 병합 옵션 및 컴파일된 쿼리