Share via


Queryuitvoering

Nadat een LINQ-query is gemaakt door een gebruiker, wordt deze geconverteerd naar een opdrachtstructuur. Een opdrachtstructuur is een weergave van een query die compatibel is met entity framework. De opdrachtstructuur wordt vervolgens uitgevoerd op basis van de gegevensbron. Tijdens de uitvoering van query's worden alle query-expressies (dat wil gezegd alle onderdelen van de query) geëvalueerd, met inbegrip van expressies die worden gebruikt bij de materialisatie van resultaten.

Op welk punt query-expressies worden uitgevoerd, kan variëren. LINQ-query's worden altijd uitgevoerd wanneer de queryvariabele wordt overschreven, niet wanneer de queryvariabele wordt gemaakt. Dit wordt de uitgestelde uitvoering genoemd. U kunt ook afdwingen dat een query onmiddellijk wordt uitgevoerd, wat handig is voor het opslaan van queryresultaten in de cache. Dit wordt verderop in dit onderwerp beschreven.

Wanneer een LINQ naar entiteiten-query wordt uitgevoerd, worden sommige expressies in de query mogelijk uitgevoerd op de server en worden sommige onderdelen mogelijk lokaal op de client uitgevoerd. Evaluatie van een expressie aan de clientzijde vindt plaats voordat de query op de server wordt uitgevoerd. Als een expressie wordt geëvalueerd op de client, wordt het resultaat van die evaluatie vervangen door de expressie in de query en wordt de query vervolgens uitgevoerd op de server. Omdat query's worden uitgevoerd op de gegevensbron, overschrijft de configuratie van de gegevensbron het gedrag dat is opgegeven in de client. De verwerking van null-waarden en de numerieke precisie zijn bijvoorbeeld afhankelijk van de serverinstellingen. Eventuele uitzonderingen die optreden tijdens het uitvoeren van query's op de server, worden rechtstreeks doorgegeven aan de client.

Tip

Zie Classificatie van Standard-queryoperators op wijze van uitvoering (C#) voor een handig overzicht van queryoperators in tabelindeling, waarmee u snel het uitvoeringsgedrag van een operator kunt identificeren.

Uitgestelde queryuitvoering

In een query die een reeks waarden retourneert, bevat de queryvariabele zelf nooit de queryresultaten en worden alleen de queryopdrachten opgeslagen. De uitvoering van de query wordt uitgesteld totdat de queryvariabele wordt herhaald in een foreach of For Each lus. Dit wordt de uitgestelde uitvoering genoemd. Dat wil gezegd, de uitvoering van query's vindt enige tijd plaats nadat de query is gemaakt. Dit betekent dat u zo vaak als u wilt een query kunt uitvoeren. Dit is handig wanneer u bijvoorbeeld een database hebt die wordt bijgewerkt door andere toepassingen. In uw toepassing kunt u een query maken om de meest recente informatie op te halen en de query herhaaldelijk uit te voeren, waarbij de bijgewerkte gegevens telkens worden geretourneerd.

Door de uitgestelde uitvoering kunnen meerdere query's worden gecombineerd of een query worden uitgebreid. Wanneer een query wordt uitgebreid, wordt deze aangepast om de nieuwe bewerkingen op te nemen. De uiteindelijke uitvoering geeft de wijzigingen weer. In het volgende voorbeeld retourneert de eerste query alle producten. De tweede query breidt de eerste uit door Where alle producten van grootte L te retourneren:

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

Nadat een query is uitgevoerd, worden alle opeenvolgende query's gebruikt voor de LINQ-operators in het geheugen. Het herhalen van de queryvariabele met behulp van een of For Each meer foreach instructies of door een van de LINQ-conversieoperators aan te roepen, leidt tot onmiddellijke uitvoering. Deze conversieoperators omvatten het volgende: ToList, ToArray, ToLookupen ToDictionary.

Onmiddellijke uitvoering van query's

In tegenstelling tot de uitgestelde uitvoering van query's die een reeks waarden produceren, worden query's die een singleton-waarde retourneren, onmiddellijk uitgevoerd. Enkele voorbeelden van singleton-query's zijn Average, Count, Firsten Max. Deze worden onmiddellijk uitgevoerd omdat de query een reeks moet produceren om het singleton-resultaat te berekenen. U kunt ook onmiddellijke uitvoering afdwingen. Dit is handig wanneer u de resultaten van een query in de cache wilt opslaan. Als u de onmiddellijke uitvoering van een query die geen singleton-waarde produceert, wilt afdwingen, kunt u de ToList methode, de ToDictionary methode of de ToArray methode voor een query of queryvariabele aanroepen. In het volgende voorbeeld wordt de ToArray methode gebruikt om onmiddellijk een reeks in een matrix te evalueren.

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

U kunt de uitvoering ook afdwingen door de foreach of For Each lus direct na de query-expressie te plaatsen, maar door alle gegevens in één verzamelingsobject aan te roepen ToList of ToArray in de cache op te plaatsen.

Store-uitvoering

In het algemeen worden expressies in LINQ naar entiteiten geëvalueerd op de server en het gedrag van de expressie mag niet worden verwacht dat ze de semantiek van Common Language Runtime (CLR) volgen, maar die van de gegevensbron. Er zijn echter uitzonderingen op dit, bijvoorbeeld wanneer de expressie wordt uitgevoerd op de client. Dit kan onverwachte resultaten veroorzaken, bijvoorbeeld wanneer de server en client zich in verschillende tijdzones bevinden.

Sommige expressies in de query kunnen worden uitgevoerd op de client. Over het algemeen wordt verwacht dat de meeste query-uitvoering plaatsvindt op de server. Afgezien van methoden die worden uitgevoerd op query-elementen die zijn toegewezen aan de gegevensbron, zijn er vaak expressies in de query die lokaal kunnen worden uitgevoerd. Lokale uitvoering van een query-expressie levert een waarde op die kan worden gebruikt in de queryuitvoering of resultaatconstructie.

Bepaalde bewerkingen worden altijd uitgevoerd op de client, zoals binding van waarden, subexpressies, subquery's van sluitingen en materialisatie van objecten in queryresultaten. Het netto-effect hiervan is dat deze elementen (bijvoorbeeld parameterwaarden) niet kunnen worden bijgewerkt tijdens de uitvoering. Anonieme typen kunnen inline worden samengesteld op de gegevensbron, maar moeten niet worden aangenomen. Inline-groeperingen kunnen ook worden samengesteld in de gegevensbron, maar dit mag niet in elke instantie worden aangenomen. Over het algemeen is het raadzaam geen veronderstellingen te maken over wat er op de server is gebouwd.

In deze sectie worden de scenario's beschreven waarin code lokaal op de client wordt uitgevoerd. Zie Expressies in LINQ naar entiteitenquery's voor meer informatie over welke typen expressies lokaal worden uitgevoerd.

Letterlijke waarden en parameters

Lokale variabelen, zoals de orderID variabele in het volgende voorbeeld, worden geëvalueerd op de client.

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

Methodeparameters worden ook geëvalueerd op de client. De orderID parameter die is doorgegeven aan de MethodParameterExample onderstaande methode, is een voorbeeld.

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

Letterlijke gieten op de client

Casten van null naar een CLR-type wordt uitgevoerd op de client:

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

Casten naar een type, zoals een nullable Decimal, wordt uitgevoerd op de client:

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

Constructors voor letterlijke gegevens

Nieuwe CLR-typen die kunnen worden toegewezen aan conceptuele modeltypen, worden uitgevoerd op de client:

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

Er worden ook nieuwe matrices uitgevoerd op de client.

Uitzonderingen opslaan

Storefouten die zijn opgetreden tijdens het uitvoeren van query's, worden doorgegeven aan de client en worden niet toegewezen of verwerkt.

Configuratie opslaan

Wanneer de query wordt uitgevoerd in het archief, overschrijft de configuratie van het archief alle clientgedrag en worden semantiek opgeslagen voor alle bewerkingen en expressies. Dit kan leiden tot een verschil in gedrag tussen CLR en het opslaan van uitvoering op gebieden zoals null-vergelijkingen, GUID-volgorde, precisie en nauwkeurigheid van bewerkingen met niet-nauwkeurige gegevenstypen (zoals drijvendekommatypen of DateTime) en tekenreeksbewerkingen. Het is belangrijk om dit in gedachten te houden bij het onderzoeken van queryresultaten.

Hier volgen bijvoorbeeld enkele verschillen in gedrag tussen de CLR en SQL Server:

  • SQL Server orders GUID's anders dan de CLR.

  • Er kunnen ook verschillen zijn in resultaatprecisie bij het omgaan met het decimale type op SQL Server. Dit komt door de vaste precisievereisten van het decimale sql Server-type. Het gemiddelde van Decimal waarden 0,0, 0,0 en 1,0 is bijvoorbeeld 0,333333333333333333333333333 in het geheugen op de client, maar 0,333333 in het archief (op basis van de standaardprecisie voor het decimale type van SQL Server).

  • Sommige tekenreeksvergelijkingsbewerkingen worden ook anders verwerkt in SQL Server dan in de CLR. Het vergelijkingsgedrag van tekenreeksen is afhankelijk van de sorteringsinstellingen op de server.

  • Functie- of methode-aanroepen, wanneer deze zijn opgenomen in een LINQ naar entiteitenquery, worden toegewezen aan canonieke functies in het Entity Framework, die vervolgens worden vertaald naar Transact-SQL en worden uitgevoerd op de SQL Server-database. Er zijn gevallen waarin het gedrag van deze toegewezen functies kan verschillen van de implementatie in de basisklassebibliotheken. Als u bijvoorbeeld de Contains, StartsWithen EndsWith methoden aanroept met een lege tekenreeks als parameter, wordt deze geretourneerd true wanneer deze wordt uitgevoerd in de CLR, maar wordt geretourneerd false wanneer deze wordt uitgevoerd in SQL Server. De EndsWith methode kan ook verschillende resultaten retourneren omdat SQL Server twee tekenreeksen als gelijk beschouwt als ze alleen verschillen in volgspaties, terwijl de CLR van mening is dat ze niet gelijk zijn. Dit wordt geïllustreerd in het volgende voorbeeld:

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