Gecompileerde query's (LINQ naar entiteiten)
Wanneer u een toepassing hebt die structureel vergelijkbare query's uitvoert in Entity Framework, kunt u de prestaties vaak verhogen door de query eenmalig te compileren en meerdere keren uit te voeren met verschillende parameters. Een toepassing moet bijvoorbeeld mogelijk alle klanten in een bepaalde stad ophalen; de plaats wordt opgegeven tijdens uitvoering door de gebruiker in een formulier. LINQ to Entities ondersteunt het gebruik van gecompileerde query's voor dit doel.
Vanaf .NET Framework 4.5 worden LINQ-query's automatisch in de cache opgeslagen. U kunt echter nog steeds gecompileerde LINQ-query's gebruiken om deze kosten in latere uitvoeringen te verlagen en gecompileerde query's kunnen efficiënter zijn dan LINQ-query's die automatisch in de cache worden opgeslagen. LINQ op entiteitenquery's die de operator toepassen op in-memory verzamelingen, worden niet automatisch in de Enumerable.Contains
cache opgeslagen. Het parameteriseren van in-memory verzamelingen in gecompileerde LINQ-query's is ook niet toegestaan.
De CompiledQuery klasse biedt compilatie en caching van query's voor hergebruik. Conceptueel gezien bevat deze klasse een CompiledQuerymethode Compile
met verschillende overbelastingen. Roep de Compile
methode aan om een nieuwe gemachtigde te maken die de gecompileerde query vertegenwoordigt. De Compile
methoden, geleverd met een ObjectContext en parameterwaarden, retourneren een gemachtigde die een bepaald resultaat produceert (zoals een IQueryable<T> exemplaar). De query wordt één keer gecompileerd tijdens de eerste uitvoering. De samenvoegopties die zijn ingesteld voor de query op het moment van de compilatie, kunnen later niet meer worden gewijzigd. Zodra de query is gecompileerd, kunt u alleen parameters van het primitieve type opgeven, maar u kunt geen onderdelen van de query vervangen die de gegenereerde SQL zouden wijzigen. Zie OPTIES voor EF-samenvoeging en gecompileerde query's voor meer informatie.
De LINQ to Entities-queryexpressie die door de CompiledQueryCompile
methode wordt gecompileerd, wordt vertegenwoordigd door een van de algemene Func
gemachtigden, zoals Func<T1,T2,T3,T4,TResult>. De query-expressie kan maximaal een ObjectContext
parameter, een retourparameter en 16 queryparameters inkapselen. Als er meer dan 16 queryparameters vereist zijn, kunt u een structuur maken waarvan de eigenschappen queryparameters vertegenwoordigen. U kunt vervolgens de eigenschappen van de structuur in de query-expressie gebruiken nadat u de eigenschappen hebt ingesteld.
Voorbeeld 1
In het volgende voorbeeld wordt een query gecompileerd en aangeroepen die een Decimal invoerparameter accepteert en een reeks orders retourneert waarbij het totale verschuldigde bedrag groter is dan of gelijk is aan $ 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: {0} Order date: {1} Total due: {2}",
order.SalesOrderID,
order.OrderDate,
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
Voorbeeld 2
In het volgende voorbeeld wordt een query gecompileerd en vervolgens een query aangeroepen die een ObjectQuery<T> exemplaar retourneert:
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
Voorbeeld 3
In het volgende voorbeeld wordt een query gecompileerd en vervolgens een query aangeroepen waarmee het gemiddelde van de prijzen van de productlijst als een Decimal waarde wordt geretourneerd:
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 $: {0}", 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
Voorbeeld 4
In het volgende voorbeeld wordt een query gecompileerd en aangeroepen die een String invoerparameter accepteert en vervolgens een Contact
wiens e-mailadres begint met de opgegeven tekenreeks:
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': {0}",
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
Voorbeeld 5
In het volgende voorbeeld wordt een query gecompileerd en aangeroepen die parameters accepteert DateTime en Decimal invoert en een reeks orders retourneert waarbij de orderdatum later is dan 8 maart 2003 en het totale verschuldigde bedrag kleiner is dan $ 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: {0} Order date: {1} Total due: {2}", order.SalesOrderID, order.OrderDate, 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
Voorbeeld 6
In het volgende voorbeeld wordt een query gecompileerd en vervolgens aangeroepen die een DateTime invoerparameter accepteert en een reeks orders retourneert waarbij de orderdatum later is dan 8 maart 2004. Deze query retourneert de ordergegevens als een reeks anonieme typen. Anonieme typen worden afgeleid door de compiler, dus u kunt geen typeparameters opgeven in de CompiledQuerymethode en Compile
het type wordt gedefinieerd in de query zelf.
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: {0} Order date: {1} Total due: {2}", order.SalesOrderID, order.OrderDate, 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
Voorbeeld 7
In het volgende voorbeeld wordt een query gecompileerd en vervolgens aangeroepen die een door de gebruiker gedefinieerde structuurinvoerparameter accepteert en een reeks orders retourneert. De structuur definieert de begindatum, einddatum en de totale einddatum van de queryparameters en de query retourneert orders die zijn verzonden tussen 3 en 8 maart 2003 met een totaal verschuldigde waarde van meer dan $ 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: {0}", sale.SalesOrderID);
Console.WriteLine("Ship date: {0}", sale.ShipDate);
Console.WriteLine("Total due: {0}", 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
De structuur waarmee de queryparameters worden gedefinieerd:
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