Compartir a través de


Ejecución de la consulta

Una vez creada por un usuario una consulta LINQ, se convierte en un árbol de comandos. Un árbol de comandos es una representación de una consulta que es compatible con Entity Framework. Posteriormente, el árbol de comandos se ejecuta en el origen de datos. En el momento de la ejecución de la consulta, se evalúan todas las expresiones de consulta (es decir, todos los componentes de la consulta), incluidas las expresiones que se utilizan en la materialización del resultado.

El momento en que se ejecutan las expresiones de consulta puede variar. Las consultas LINQ siempre se ejecutan cuando se recorre en iteración la variable de consulta, no cuando se crea la citada variable de consulta. Esto se denomina ejecución aplazada. También se puede obligar a que la consulta se ejecute inmediatamente, lo que es útil para almacenar en caché los resultados de la consulta. Esto se describe más adelante en este tema.

Cuando se ejecuta una consulta de LINQ to Entities, algunas de sus expresiones pueden ejecutarse en el servidor y ciertas partes pueden ejecutarse de forma local en el cliente. La evaluación en el cliente de una expresión se lleva a cabo antes de ejecutar la consulta en el servidor. Si una expresión se evalúa en el cliente, el resultado de esa evaluación se sustituye por la expresión en la consulta, y ésta se ejecuta entonces en el servidor. Dado que las consultas se ejecutan en el origen de datos, la configuración de este invalida el comportamiento especificado en el cliente. Por ejemplo, la precisión numérica y el tratamiento de los valores NULL dependen de la configuración de servidor. Cualquier excepción que se produzca durante la ejecución de la consulta en el servidor se pasa directamente al cliente.

Ejecución aplazada

La propia variable de consulta solo almacena los comandos de consulta cuando la consulta está diseñada para devolver una secuencia de valores. Si la consulta no contiene un método que produzca una ejecución inmediata, la ejecución real de la consulta se aplaza hasta que la variable de consulta se recorre en iteración en un bucle foreach o For Each. La consulta se ejecuta en el servidor cada vez que se recorre en iteración la variable de consulta en un bucle foreach o For Each. Esto se denomina ejecución aplazada. La ejecución aplazada permite combinar varias consultas o ampliar una consulta. Cuando se produce esta circunstancia, la consulta se modifica para incluir las nuevas operaciones. La ejecución real reflejará los cambios. La propia variable de consulta nunca contiene los resultados de la consulta. Esto significa que se puede ejecutar una consulta con la frecuencia que se desee. Por ejemplo, se puede tener una base de datos que esté siendo actualizada de forma continua por una aplicación independiente. En la aplicación, se puede crear una consulta que recupere los datos más recientes y se puede ejecutar repetidamente de acuerdo con un intervalo; los resultados recuperados serán diferentes cada vez.

Las consultas de LINQ to Entities se convierten en árboles de comandos en Entity Framework y se ejecutan con el origen de datos cuando se produce una iteración en los resultados. En este momento, los errores de la conversión darán lugar a excepciones que se producirán en el cliente.

Ejecución inmediata

A diferencia de la ejecución aplazada de consultas que producen una secuencia de valores, las consultas que devuelven un valor singleton se ejecutan inmediatamente. Algunos ejemplos de consultas singleton son Average, Count, First y Max. Se ejecutan inmediatamente porque la consulta debe producir una secuencia para calcular el resultado singleton. También se puede forzar la ejecución inmediata. Esto es útil cuando se desea almacenar en memoria caché los resultados de una consulta. Para forzar la ejecución inmediata de una consulta que no produce un valor singleton, se puede llamar a los métodos ToList, ToDictionary o ToArray en una consulta o una variable de consulta. En el ejemplo siguiente se utiliza el método ToArray para evaluar de forma inmediata una secuencia en una matriz.

Using AWEntities As New AdventureWorksEntities
    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    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
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    ObjectQuery<Product> products = AWEntities.Product;

    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);
    }
}

También se puede forzar la ejecución colocando el bucle foreach o For Each inmediatamente después de la expresión de consulta, pero si se llama a los métodos ToList o ToArray, se almacenarán en caché todos los datos de un solo objeto de la colección.

Ejecución en el almacén

En general, las expresiones de LINQ to Entities se evalúan en el servidor, y no es de esperar que el comportamiento de la expresión siga la semántica de Common Language Runtime (CLR), sino la del origen. Sin embargo, hay excepciones, como cuando la expresión se ejecuta en el cliente. Esto puede producir resultados inesperados, por ejemplo cuando el servidor y el cliente están en zonas horarias diferentes.

Algunas expresiones de la consulta se pueden ejecutar en el cliente. En general, se espera que la mayor parte de la ejecución de la consulta se produzca en el servidor. Además de los métodos ejecutados en elementos de consulta asignados al origen de datos, suele haber expresiones de la consulta que se pueden ejecutar localmente. La ejecución local de una expresión de consulta produce un valor que se puede utilizar en la ejecución de la consulta o en la generación del resultado.

Ciertas operaciones se ejecutan siempre en el cliente, como el enlace de valores, subexpresiones, subconsultas de cierres, y la materialización de objetos en los resultados de la consulta. La consecuencia final es que estos elementos (por ejemplo, los valores de parámetro) no se pueden actualizar durante la ejecución. Los tipos anónimos se pueden crear insertados en el origen de datos, pero no se debe suponer que esto se vaya a producir. Las agrupaciones insertadas también se pueden crear en el almacén, pero no se debe suponer que esto tenga lugar en cada instancia. En general, es preferible no hacer suposiciones sobre lo que se crea en el servidor.

En esta sección se describen los escenarios en que el código se ejecuta localmente en el cliente. Para obtener más información sobre qué tipos de expresiones se ejecutan localmente, vea Expresiones en consultas de LINQ to Entities.

Literales y parámetros

Las variables locales, como la variable orderID del ejemplo siguiente, se evalúan en el cliente.

Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

Dim orderID As Integer = 51987

Dim salesInfo = _
    From s In sales _
    Where s.SalesOrderID = orderID _
    Select s
ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;

int orderID = 51987;

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

Los parámetros de métodos también se evalúan en el cliente. El parámetro orderID que se pasa al método MethodParameterExample, como se puede apreciar más abajo, es un ejemplo.

Function MethodParameterExample(ByVal orderID As Integer)
    Using AWEntities As New AdventureWorksEntities()

        Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

        Dim salesInfo = _
            From s In sales _
            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
public static void MethodParameterExample(int orderID)
{
    using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
    {
        
        ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;
                        
        IQueryable<SalesOrderHeader> salesInfo =
            from s in sales
            where s.SalesOrderID == orderID
            select s;                

        foreach (SalesOrderHeader sale in salesInfo)
        {
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
        }
    }
}

Convertir literales en el cliente

La conversión de null a un tipo CLR se ejecuta en el cliente:

Dim contacts As ObjectQuery(Of Contact) = AWEntities.Contact

Dim query = _
    From c In contacts _
    Where c.EmailAddress = CType(Nothing, String) _
    Select c
ObjectQuery<Contact> contacts = AWEntities.Contact;

IQueryable<Contact> query =
    from c in contacts
    where c.EmailAddress == (string)null
    select c;

La conversión a un tipo, como un Decimal que acepta valores NULL, se ejecuta en el cliente:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
        Where product.Weight = CType(23.77, Decimal?) _
        Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == (decimal?)23.77
    select product;

Constructores para literales

Los nuevos tipos CLR que se pueden asignar a tipos EDM se ejecutan en el cliente:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
    Where product.Weight = New Decimal(23.77) _
    Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == new decimal(23.77)
    select product;

Las nuevas matrices también se ejecutan en el cliente.

Excepciones en el almacén

Los errores en el almacén que tienen lugar durante la ejecución de la consulta se pasan al cliente y no se asignan ni controlan.

Configuración del almacén

Cuando la consulta se ejecuta en el almacén, la configuración del almacén invalida todos los comportamientos del cliente, y la semántica del almacén se expresa para todas las operaciones y expresiones. El resultado puede ser una diferencia de comportamiento entre la ejecución en el CLR y la ejecución en el almacén en áreas como las comparaciones de NULL, la ordenación de GUID, la precisión y la exactitud de las operaciones que afectan a tipos de datos no precisos (como los tipos de punto flotante o DateTime) y las operaciones de cadena. Es importante tener esto en cuenta al examinar los resultados de la consulta.

Por ejemplo, a continuación se indican algunas diferencias de comportamiento entre CLR y SQL Server:

  • SQL Server ordena los GUID de manera diferente que CLR.

  • También puede haber diferencias en la precisión del resultado cuando se trabaja con el tipo Decimal en SQL Server. Esto se debe a los requisitos de precisión fijados para el tipo decimal de SQL Server. Por ejemplo, el valor medio de los valores Decimal 0.0, 0.0, y 1.0 es 0.3333333333333333333333333333 en memoria en el cliente, pero 0.333333 en el almacén (si se toma como base la precisión predeterminada para el tipo decimal de SQL Server).

  • Algunas operaciones de comparación de cadenas también se tratan en SQL Server de forma diferente que en CLR. El comportamiento de la comparación de cadenas depende de los valores de intercalación en el servidor.

  • Las llamadas a funciones o métodos, cuando se incluyen en una consulta de LINQ to Entities, se asignan a funciones canónicas en Entity Framework, que, posteriormente, se convierten a Transact-SQL y se ejecutan en la base de datos de SQL Server. Hay casos en que el comportamiento que muestran estas funciones asignadas puede diferir de la implementación en las bibliotecas de clases base. Por ejemplo, una llamada a los métodos Contains, EndsWith y StartsWith con una cadena vacía como parámetro, devolverá true si se ejecuta en CLR pero devolverá false si se ejecuta en SQL Server. El método EndsWith también puede devolver resultados diferentes porque SQL Server considera que dos cadenas son iguales si sólo se diferencian en el espacio en blanco final, mientras que CLR considera que no son iguales. Esto se muestra en el ejemplo siguiente:

Using AWEntities As New AdventureWorksEntities()

    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    Dim query = _
        From p In 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
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{

    ObjectQuery<Product> products = AWEntities.Product;

    IQueryable<string> query = from p in 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 "));

}

Vea también

Otros recursos

Consultar con LINQ to Entities