Delen via


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

Zie ook