Lekérdezés végrehajtása

Miután egy felhasználó létrehozta a LINQ-lekérdezést, a rendszer parancsfává alakítja. A parancsfa az Entity Frameworkdel kompatibilis lekérdezések ábrázolása. A parancsfa ezután az adatforráson lesz végrehajtva. A lekérdezés végrehajtásakor a rendszer kiértékeli az összes lekérdezési kifejezést (azaz a lekérdezés összes összetevőjét), beleértve azokat a kifejezéseket is, amelyeket az eredmény materializálásához használnak.

A lekérdezési kifejezések végrehajtása attól függően változhat, hogy mikor lesznek végrehajtva. A LINQ-lekérdezések mindig akkor lesznek végrehajtva, ha a lekérdezési változó iterated over, és nem a lekérdezési változó létrehozásakor. Ezt halasztott végrehajtásnak nevezzük. A lekérdezéseket azonnali végrehajtásra is kényszerítheti, ami hasznos lehet a lekérdezés eredményeinek gyorsítótárazásához. Ezt a témakör későbbi részében ismertetjük.

A LINQ–Entitások lekérdezés végrehajtásakor előfordulhat, hogy a lekérdezés egyes kifejezései a kiszolgálón lesznek végrehajtva, és egyes részek helyileg is végrehajthatók az ügyfélen. A kifejezés ügyféloldali kiértékelése a lekérdezés kiszolgálón való végrehajtása előtt történik. Ha egy kifejezés kiértékelése történik az ügyfélen, a kiértékelés eredménye a lekérdezésben szereplő kifejezés helyett a kiszolgálón lesz végrehajtva. Mivel a lekérdezések az adatforráson vannak végrehajtva, az adatforrás konfigurációja felülírja az ügyfélben megadott viselkedést. A null érték kezelése és a numerikus pontosság például a kiszolgáló beállításaitól függ. A lekérdezés végrehajtása során a kiszolgálóra vonatkozó kivételek közvetlenül az ügyfélnek lesznek átadva.

Tipp.

Az operátorok végrehajtási viselkedésének gyors azonosítását lehetővé tévő táblázatformátumú lekérdezési operátorok kényelmes összefoglalását lásd: Standard lekérdezési operátorok besorolása végrehajtási mód szerint (C#).

Halasztott lekérdezés végrehajtása

Egy olyan lekérdezésben, amely értéksorozatot ad vissza, maga a lekérdezési változó soha nem tárolja a lekérdezés eredményeit, és csak a lekérdezési parancsokat tárolja. A lekérdezés végrehajtása elhalasztva, amíg a lekérdezési változó át nem kerül egy foreach vagy For Each több ciklusba. Ezt halasztott végrehajtásnak nevezzük, vagyis a lekérdezés végrehajtása a lekérdezés létrehozása után egy ideig történik. Ez azt jelenti, hogy a lekérdezéseket a kívánt gyakoriság szerint hajthatja végre. Ez akkor hasznos, ha például olyan adatbázissal rendelkezik, amelyet más alkalmazások frissítenek. Az alkalmazásban létrehozhat egy lekérdezést, amely lekéri a legfrissebb információkat, és ismételten végrehajtja a lekérdezést, és minden alkalommal visszaadja a frissített információkat.

Halasztott végrehajtás esetén több lekérdezés is kombinálható, vagy egy lekérdezés bővíthető. Ha egy lekérdezés ki van terjesztve, a rendszer úgy módosítja, hogy az tartalmazza az új műveleteket, és a végleges végrehajtás tükrözni fogja a módosításokat. Az alábbi példában az első lekérdezés az összes terméket visszaadja. A második lekérdezés kibővíti az elsőt Where az "L" méretű termékek visszaadásával:

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    IQueryable<Product> productsQuery =
        from p in context.Products
        select p;

    IQueryable<Product> largeProducts = productsQuery.Where(p => p.Size == "L");

    Console.WriteLine("Products of size 'L':");
    foreach (var product in largeProducts)
    {
        Console.WriteLine(product.Name);
    }
}
Using context As New AdventureWorksEntities()
    Dim productsQuery = _
        From p In context.Products _
        Select p

    Dim largeProducts = _
        productsQuery.Where(Function(p) p.Size = "L")

    Console.WriteLine("Products of size 'L':")
    For Each product In largeProducts
        Console.WriteLine(product.Name)
    Next
End Using

A lekérdezés végrehajtása után az egymást követő lekérdezések a memóriában lévő LINQ-operátorokat használják. Ha a lekérdezési változót egy vagy utasítással foreach vagy For Each a LINQ-konverziós operátorok egyikének meghívásával hajtja végre, az azonnali végrehajtást fog eredményezni. Ezek a konverziós operátorok a következők: ToList, ToArray, ToLookupés ToDictionary.

Azonnali lekérdezés végrehajtása

Az értékek sorozatát eredményező lekérdezések halasztott végrehajtásával ellentétben a rendszer azonnal végrehajtja az egyszeri értéket visszaadó lekérdezéseket. Néhány példa az egytáblás lekérdezésekreAverage: , Countés FirstMax. Ezek azonnal végrehajthatók, mert a lekérdezésnek egy sorozatot kell létrehoznia az egyszeri eredmény kiszámításához. Azonnali végrehajtást is kényszeríthet. Ez akkor hasznos, ha egy lekérdezés eredményeit gyorsítótárba szeretné helyezni. Egy olyan lekérdezés azonnali végrehajtásának kényszerítéséhez, amely nem állít elő egyetlen értéket, meghívhatja a ToList metódust, a ToDictionary metódust vagy a ToArray metódust egy lekérdezésen vagy lekérdezési változón. Az alábbi példa a ToArray metódus használatával azonnal kiértékel egy sorozatot egy tömbbe.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    ObjectSet<Product> products = context.Products;

    Product[] prodArray = (
        from product in products
        orderby product.ListPrice descending
        select product).ToArray();

    Console.WriteLine("Every price from highest to lowest:");
    foreach (Product product in prodArray)
    {
        Console.WriteLine(product.ListPrice);
    }
}
Using context As New AdventureWorksEntities
    Dim products As ObjectSet(Of Product) = context.Products

    Dim prodArray As Product() = ( _
        From product In products _
        Order By product.ListPrice Descending _
        Select product).ToArray()

    Console.WriteLine("The list price from highest to lowest:")
    For Each prod As Product In prodArray
        Console.WriteLine(prod.ListPrice)
    Next
End Using

A végrehajtást úgy is kényszerítheti, hogy közvetlenül a lekérdezési kifejezés után helyezi el a foreachFor Each ciklust, de meghívja ToList vagy ToArray gyorsítótárazza az összes adatot egyetlen gyűjteményobjektumban.

Tárvégrehajtás

A LINQ-entitások kifejezéseinek kiértékelése általában a kiszolgálón történik, és a kifejezés viselkedésének nem a közös nyelvi futtatókörnyezet (CLR) szemantikáját kell követnie, hanem az adatforráséit. Vannak azonban kivételek, például amikor a kifejezés végrehajtása az ügyfélen történik. Ez váratlan eredményeket okozhat, például ha a kiszolgáló és az ügyfél különböző időzónákban van.

Előfordulhat, hogy a lekérdezés egyes kifejezései végrehajthatók az ügyfélen. A legtöbb lekérdezés végrehajtása általában a kiszolgálón várható. Az adatforrásra leképezett lekérdezési elemeken végrehajtott metódusokon kívül a lekérdezésben gyakran vannak olyan kifejezések, amelyek helyileg végrehajthatók. A lekérdezési kifejezések helyi végrehajtása olyan értéket eredményez, amely felhasználható a lekérdezés végrehajtásához vagy az eredmény létrehozásához.

Bizonyos műveletek mindig az ügyfélen lesznek végrehajtva, például az értékek kötése, az alkifejezések, a lezárásokból származó allekérdezések és az objektumok a lekérdezési eredményekbe való materializálása. Ennek nettó hatása, hogy ezek az elemek (például paraméterértékek) nem frissíthetők a végrehajtás során. A névtelen típusok beágyazottan is létrehozhatóak az adatforráson, de nem szabad feltételezni, hogy ez így van. A beágyazott csoportosítások az adatforrásban is létrehozhatók, de ezt nem szabad minden esetben feltételezni. Általában a legjobb, ha nem feltételezi, hogy mi épül fel a kiszolgálón.

Ez a szakasz azokat a forgatókönyveket ismerteti, amelyekben a kód helyileg lesz végrehajtva az ügyfélen. A kifejezéstípusok helyi végrehajtásáról további információt a LINQ-kifejezések entitáslekérdezések között című témakörben talál.

Literálok és paraméterek

Az ügyfél kiértékeli a helyi változókat, például a orderID következő példában szereplő változót.

int orderID = 51987;

IQueryable<SalesOrderHeader> salesInfo =
    from s in context.SalesOrderHeaders
    where s.SalesOrderID == orderID
    select s;
Dim orderID As Integer = 51987

Dim salesInfo = _
    From s In context.SalesOrderHeaders _
    Where s.SalesOrderID = orderID _
    Select s

A metódusparaméterek kiértékelése az ügyfélen is történik. Az orderID alábbi metódusnak MethodParameterExample átadott paraméter egy példa.

public static void MethodParameterExample(int orderID)
{
    using (AdventureWorksEntities context = new AdventureWorksEntities())
    {

        IQueryable<SalesOrderHeader> salesInfo =
            from s in context.SalesOrderHeaders
            where s.SalesOrderID == orderID
            select s;

        foreach (SalesOrderHeader sale in salesInfo)
        {
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
        }
    }
}
Function MethodParameterExample(ByVal orderID As Integer)
    Using context As New AdventureWorksEntities()

        Dim salesInfo = _
            From s In context.SalesOrderHeaders _
            Where s.SalesOrderID = orderID _
            Select s

        Console.WriteLine("Sales order info:")
        For Each sale As SalesOrderHeader In salesInfo
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue)
        Next
    End Using

End Function

Literálok kiosztása az ügyfélen

A rendszer az ügyfélen hajtja végre a CLR-típusra null történő kiosztást:

IQueryable<Contact> query =
    from c in context.Contacts
    where c.EmailAddress == (string)null
    select c;
Dim query = _
    From c In context.Contacts _
    Where c.EmailAddress = CType(Nothing, String) _
    Select c

Egy típusra( például null értékűre Decimal) történő kiosztás végrehajtása az ügyfélen történik:

var weight = (decimal?)23.77;
IQueryable<Product> query =
    from product in context.Products
    where product.Weight == weight
    select product;
Dim weight = CType(23.77, Decimal?)
Dim query = _
    From product In context.Products _
    Where product.Weight = weight _
    Select product

Konstruktorok literálokhoz

A koncepcionális modelltípusokra leképezhető új CLR-típusok végrehajtása az ügyfélen történik:

var weight = new decimal(23.77);
IQueryable<Product> query =
    from product in context.Products
    where product.Weight == weight
    select product;
Dim weight = New Decimal(23.77)
Dim query = _
    From product In context.Products _
    Where product.Weight = weight _
    Select product

A rendszer új tömböket is végrehajt az ügyfélen.

Kivételek tárolása

A lekérdezés végrehajtása során felmerülő tárolási hibák az ügyfélnek lesznek átadva, és nem lesznek leképezve vagy kezelve.

Tárolókonfiguráció

Amikor a lekérdezés az áruházban fut, az áruház konfigurációja felülírja az összes ügyfél viselkedését, és a tároló szemantikája minden művelethez és kifejezéshez kifejezve lesz. Ez eltérő viselkedést eredményezhet a CLR és a tárolóvégrehajtás között olyan területeken, mint a null összehasonlítások, a GUID-sorrend, a pontosság és a pontosság a nem pontos adattípusokat (például lebegőpontos típusok vagy DateTime) és sztringműveleteket tartalmazó műveletek esetében. Ezt fontos szem előtt tartani a lekérdezési eredmények vizsgálatakor.

Az alábbiakban például a CLR és az SQL Server közötti viselkedésbeli különbségek szerepelnek:

  • Az SQL Server a CLR-hez képest eltérő grafikus GUID-ket rendel.

  • Az eredmény pontossága is eltérő lehet az SQL Server tizedestípusának kezelésekor. Ennek oka az SQL Server decimális típusának rögzített pontossági követelményei. A 0.0, 0.0 és 1.0 értékek átlaga Decimal például 0,3333333333333333333333333333333333333333333333 a memóriában, de az adattárban 0,33333333 (az SQL Server decimális típusának alapértelmezett pontossága alapján).

  • Egyes sztring-összehasonlító műveleteket az SQL Server is másképp kezel, mint a CLR-ben. A sztringek összehasonlításának viselkedése a kiszolgáló rendezési beállításaitól függ.

  • A függvény- vagy metódushívások, ha szerepelnek a LINQ entitások lekérdezésében, az Entity Framework kanonikus függvényeihez vannak leképezve, amelyeket aztán lefordítanak a Transact-SQL-re, és végrehajtanak az SQL Server-adatbázisban. Vannak olyan esetek, amikor a megfeleltetett függvények viselkedése eltérhet az alaposztálykódtárak implementációjától. Ha például paraméterként egy üres sztringgel hívja meg a , és metódusokat, az a CLR-ben való végrehajtáskor fog visszatérnitrue, de az SQL Serveren való végrehajtáskor vissza fog térnifalse.EndsWithStartsWithContains A EndsWith metódus eltérő eredményeket is visszaadhat, mivel az SQL Server két sztringet egyenlőnek tekint, ha csak a záró szóközben különböznek, míg a CLR nem egyenlőnek tekinti őket. Ezt a következő példa szemlélteti:

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    IQueryable<string> query = from p in context.Products
                               where p.Name == "Reflector"
                               select p.Name;

    IEnumerable<bool> q = query.Select(c => c.EndsWith("Reflector "));

    Console.WriteLine("LINQ to Entities returns: " + q.First());
    Console.WriteLine("CLR returns: " + "Reflector".EndsWith("Reflector "));
}
Using context As New AdventureWorksEntities()

    Dim query = _
        From p In context.Products _
        Where p.Name = "Reflector" _
        Select p.Name

    Dim q = _
        query.Select(Function(c) c.EndsWith("Reflector "))

    Console.WriteLine("LINQ to Entities returns: " & q.First())
    Console.WriteLine("CLR returns: " & "Reflector".EndsWith("Reflector "))
End Using