Requêtes compilées (LINQ to Entities)Compiled Queries (LINQ to Entities)

Lorsque vous possédez une application qui exécute de nombreuses fois des requêtes similaires d’un point de vue structurel dans Entity Framework, vous pouvez souvent améliorer les performances en compilant la requête une fois et en l’exécutant plusieurs fois avec des paramètres différents.When you have an application that executes structurally similar queries many times in the Entity Framework, you can frequently increase performance by compiling the query one time and executing it several times with different parameters. Par exemple, une application peut avoir besoin de récupérer tous les clients d'une ville spécifique ; la ville est spécifiée à l'exécution par l'utilisateur dans un formulaire.For example, an application might have to retrieve all the customers in a particular city; the city is specified at runtime by the user in a form. À cette fin, LINQ to Entities prend en charge l'utilisation des requêtes compilées.LINQ to Entities supports using compiled queries for this purpose.

Depuis le .NET Framework 4.5, les requêtes LINQ sont mises en cache automatiquement.Starting with the .NET Framework 4.5, LINQ queries are cached automatically. Cependant, vous pouvez toujours utiliser des requêtes LINQ compilées pour réduire ce coût dans les exécutions ultérieures et les requêtes compilées peuvent être plus efficaces que les requêtes LINQ qui sont automatiquement mises en cache.However, you can still use compiled LINQ queries to reduce this cost in later executions and compiled queries can be more efficient than LINQ queries that are automatically cached. Notez que les requêtes LINQ to Entities qui appliquent l’opérateur Enumerable.Contains aux collections en mémoire ne sont pas automatiquement mises en cache.Note that LINQ to Entities queries that apply the Enumerable.Contains operator to in-memory collections are not automatically cached. Le paramétrage des collections en mémoire dans les requêtes LINQ compilées n’est pas autorisé.Also parameterizing in-memory collections in compiled LINQ queries is not allowed.

La classe CompiledQuery permet la compilation et la mise en cache des requêtes en vue de leur réutilisation.The CompiledQuery class provides compilation and caching of queries for reuse. Conceptuellement, cette classe contient une méthode CompiledQuery's Compile avec plusieurs surcharges.Conceptually, this class contains a CompiledQuery's Compile method with several overloads. Appelez la méthode Compile pour créer un nouveau délégué pour représenter la requête compilée.Call the Compile method to create a new delegate to represent the compiled query. Les méthodes Compile, fournies avec un objet ObjectContext et des valeurs de paramètres, retournent un délégué qui produit un résultat (tel qu'une instance de IQueryable<T>).The Compile methods, provided with a ObjectContext and parameter values, return a delegate that produces some result (such as an IQueryable<T> instance). La requête compile une fois durant la première exécution uniquement.The query compiles once during only the first execution. Les options de fusion définies pour la requête au moment de la compilation ne peuvent pas être modifiées ultérieurement.The merge options set for the query at the time of the compilation cannot be changed later. Une fois la requête compilée vous pouvez seulement fournir des paramètres de type primitif, mais vous ne pouvez pas remplacer des parties de la requête qui modifieraient le SQL généré.Once the query is compiled you can only supply parameters of primitive type but you cannot replace parts of the query that would change the generated SQL. Pour plus d’informations, consultez Options de fusion Entity Framework et requêtes compiléesFor more information, see Entity Framework Merge Options and Compiled Queries

L’expression LINQ to Entities requête qui la CompiledQueryde Compile méthode Compile est représentée par un des génériques Func délégués, tels que Func<T1,T2,T3,T4,TResult>.The LINQ to Entities query expression that the CompiledQuery's Compile method compiles is represented by one of the generic Func delegates, such as Func<T1,T2,T3,T4,TResult>. Au maximum, l'expression de requête peut encapsuler un paramètre ObjectContext, un paramètre de retour et seize paramètres de requête.At most, the query expression can encapsulate an ObjectContext parameter, a return parameter, and 16 query parameters. Si plus de seize paramètres de requête sont requis, vous pouvez créer une structure dont les propriétés représentent des paramètres de requête.If more than 16 query parameters are required, you can create a structure whose properties represent query parameters. Vous pouvez alors utiliser les propriétés sur la structure dans l'expression de requête une fois les propriétés définies.You can then use the properties on the structure in the query expression after you set the properties.

ExempleExample

L'exemple suivant compile puis appelle une requête qui accepte un paramètre d'entrée Decimal et retourne une séquence de commandes où le montant total dû est supérieur à 200,00 $ :The following example compiles and then invokes a query that accepts a Decimal input parameter and returns a sequence of orders where the total due is greater than or equal to $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

ExempleExample

L'exemple suivant compile puis appelle une requête qui retourne une instance de ObjectQuery<T> :The following example compiles and then invokes a query that returns an ObjectQuery<T> instance:

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

ExempleExample

L'exemple suivant compile puis appelle une requête qui retourne la moyenne des prix courants des produits sous la forme d'une valeur Decimal :The following example compiles and then invokes a query that returns the average of the product list prices as a Decimal value:

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

ExempleExample

L’exemple suivant compile et appelle ensuite une requête qui accepte un String d’entrée de paramètre, puis retourne un Contact dont l’adresse e-mail commence par la chaîne spécifiée :The following example compiles and then invokes a query that accepts a String input parameter and then returns a Contact whose email address starts with the specified string:

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

ExempleExample

L'exemple suivant compile puis appelle une requête qui accepte les paramètres d'entrée DateTime et Decimal et retourne une séquence de commandes où la date de commande est ultérieure au 8 mars 2003 et le montant total dû est inférieur à 300,00 $ :The following example compiles and then invokes a query that accepts DateTime and Decimal input parameters and returns a sequence of orders where the order date is later than March 8, 2003, and the total due is less than $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

ExempleExample

L'exemple suivant compile puis appelle une requête qui accepte un paramètre d'entrée DateTime et retourne une séquence de commandes dont la date de commande est postérieure au 8 mars 2004.The following example compiles and then invokes a query that accepts a DateTime input parameter and returns a sequence of orders where the order date is later than March 8, 2004. Cette requête retourne les informations de commande sous la forme d'une séquence de types anonymes.This query returns the order information as a sequence of anonymous types. Les types anonymes sont déduits par le compilateur, si bien que vous ne pouvez pas spécifier les paramètres de type dans la méthode CompiledQuery de Compile et le type est défini dans la requête elle-même.Anonymous types are inferred by the compiler, so you cannot specify type parameters in the CompiledQuery's Compile method and the type is defined in the query itself.

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

ExempleExample

L'exemple suivant compile puis appelle une requête qui accepte un paramètre d'entrée de la structure définie par l'utilisateur, puis retourne une séquence de commandes.The following example compiles and then invokes a query that accepts a user-defined structure input parameter and returns a sequence of orders. La structure définit les paramètres de requête de date de début, de date de fin et de montant total dû, et la requête retourne les commandes expédiées entre le 3 mars et le 8 mars 2003 dont le montant total dû est supérieur à 700,00 $.The structure defines start date, end date, and total due query parameters, and the query returns orders shipped between March 3 and March 8, 2003 with a total due greater than $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

La structure qui définit les paramètres de requête :The structure that defines the query parameters:

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

Voir aussiSee also