Lefordított lekérdezések (LINQ–Entitások)

Ha olyan alkalmazással rendelkezik, amely szerkezetileg hasonló lekérdezéseket hajt végre többször az Entity Frameworkben, gyakran növelheti a teljesítményt azáltal, hogy egyszer összeállítja a lekérdezést, és többször végrehajtja azt különböző paraméterekkel. Előfordulhat például, hogy egy alkalmazásnak le kell kérnie egy adott város összes ügyfelet; a várost futásidőben adja meg a felhasználó egy űrlapon. A LINQ to Entities támogatja a lefordított lekérdezések használatát erre a célra.

A 4.5-ös .NET-keretrendszer kezdve a LINQ-lekérdezések automatikusan gyorsítótárazva lesznek. A lefordított LINQ-lekérdezésekkel azonban csökkentheti ezt a költséget a későbbi végrehajtásokban, és a lefordított lekérdezések hatékonyabbak lehetnek, mint az automatikusan gyorsítótárazott LINQ-lekérdezések. A linq to Entities lekérdezések, amelyek az Enumerable.Contains operátort a memóriabeli gyűjteményekre alkalmazzák, nem lesznek automatikusan gyorsítótárazva. Emellett a memóriabeli gyűjtemények paraméterezése a lefordított LINQ-lekérdezésekben nem engedélyezett.

Az CompiledQuery osztály a lekérdezések összeállítását és gyorsítótárazását biztosítja az újrafelhasználáshoz. Elméletileg ez az osztály egy CompiledQuerytöbb Compile túlterheléssel rendelkező metódust tartalmaz. Hívja meg a Compile metódust, hogy hozzon létre egy új meghatalmazottat a lefordított lekérdezés megjelenítéséhez. Az Compile egy és paraméterértékekkel ObjectContext ellátott metódusok egy olyan delegáltat adnak vissza, amely valamilyen eredményt hoz létre (például egy példányt IQueryable<T> ). A lekérdezés csak az első végrehajtás során egyszer fordít le. A lekérdezéshez a fordítás időpontjában beállított egyesítési beállítások később nem módosíthatók. A lekérdezés fordítása után csak primitív típusú paramétereket adhat meg, de nem helyettesítheti a lekérdezés azon részeit, amelyek megváltoztatnák a létrehozott SQL-t. További információ: EF Merge Options and Compiled Queries.

A LINQ to Entities lekérdezési kifejezés, amelyet a CompiledQuerymetódus Compile lefordít, az egyik általános Func meghatalmazott képviseli, például Func<T1,T2,T3,T4,TResult>. A lekérdezési kifejezés legfeljebb egy ObjectContext paramétert, egy visszatérési paramétert és 16 lekérdezési paramétert tartalmazhat. Ha több mint 16 lekérdezési paraméterre van szükség, létrehozhat egy struktúrát, amelynek tulajdonságai lekérdezési paramétereket jelölnek. Ezután a tulajdonságok megadása után használhatja a szerkezet tulajdonságait a lekérdezési kifejezésben.

1. példa

Az alábbi példa lefordít egy lekérdezést, majd meghív egy olyan lekérdezést, amely elfogadja a Decimal bemeneti paramétert, és olyan sorrendsort ad vissza, amelyben a teljes esedékesség nagyobb vagy egyenlő, mint 200,00 USD:

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

2. példa

Az alábbi példa lefordítja, majd meghív egy példányt visszaadó lekérdezést 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. példa

Az alábbi példa lefordítja, majd meghív egy lekérdezést, amely a terméklista árainak átlagát adja vissza értékként 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 $: {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

4. példa

Az alábbi példa lefordítja, majd meghív egy lekérdezést, amely elfogadja a String bemeneti paramétert, majd visszaad egy Contact olyan e-mail-címet, amelynek az e-mail-címe a megadott sztringgel kezdődik:

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

5. példa

Az alábbi példa lefordítja és meghív egy lekérdezést, amely elfogadja DateTime és Decimal bemeneti paramétereket ad vissza, és olyan rendeléssorozatot ad vissza, amelyben a rendelés dátuma 2003. március 8-nál későbbi, és a teljes esedékesség kisebb, mint 300,00 USD:

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

6. példa

Az alábbi példa lefordít egy lekérdezést, majd meghív egy lekérdezést, amely elfogadja a DateTime bemeneti paramétert, és egy rendeléssorozatot ad vissza, ahol a rendelés dátuma 2004. március 8-nál későbbi. Ez a lekérdezés névtelen típusok sorozataként adja vissza a rendelési adatokat. A fordító névtelen típusokra hivatkozik, így a metódusban CompiledQueryCompile nem adhat meg típusparamétereket, és a típus maga a lekérdezésben van definiálva.

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

7. példa

Az alábbi példa lefordítja, majd meghív egy lekérdezést, amely elfogadja a felhasználó által definiált struktúra bemeneti paraméterét, és megrendelések sorozatát adja vissza. A struktúra meghatározza a kezdő dátumot, a befejezési dátumot és a teljes esedékes lekérdezési paramétereket, a lekérdezés pedig a 2003. március 3. és március 8. között kiszállított rendeléseket adja vissza 700,00 USD-nél nagyobb esedékességgel.

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

A lekérdezési paramétereket meghatározó struktúra:

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

Lásd még