Escribir la primera consulta con LINQ (Visual Basic)

Una consulta es una expresión que recupera datos de un origen de datos. Las consultas se expresan en un lenguaje de consulta dedicado. Con el tiempo se han desarrollado diferentes lenguajes para los distintos tipos de orígenes de datos como, por ejemplo, SQL para bases de datos relacionales y XQuery para XML. Esto hace necesario que el desarrollador de aplicaciones aprenda un nuevo lenguaje de consulta para cada tipo de origen de datos o formato de datos compatible.

Language Integrated Query (LINQ) simplifica esta situación ofreciendo un modelo coherente para trabajar con los datos de varios formatos y orígenes. En una consulta LINQ siempre se trabaja con objetos. Se usan los mismos patrones de codificación básicos para consultar y transformar datos de documentos XML, bases de datos SQL, entidades y conjuntos de datos de ADO.NET, colecciones de .NET Framework y cualquier otro formato u origen para el que haya disponible un proveedor LINQ. En este documento se describen las tres fases de creación y uso de consultas LINQ básicas.

Las tres fases de una operación de consulta

Las operaciones de consulta LINQ constan de tres acciones:

  1. Obtener el origen u orígenes de los datos.

  2. Crear la consulta.

  3. Ejecutar la consulta.

En LINQ, la ejecución de una consulta es distinta de la creación de la misma. No se recuperan datos simplemente mediante la creación de una consulta. Este punto se analiza con más detalle más adelante en este tema.

En el ejemplo siguiente se muestran las tres partes de una operación de consulta. En el ejemplo se usa una matriz de enteros como un origen de datos adecuado con fines de demostración. Sin embargo, los mismos conceptos también se aplican a otros orígenes de datos.

Nota

En Página Compilación, Diseñador de proyectos (Visual Basic), asegúrese de que Opción de inferencia está establecido en Activado.

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

Salida:

0 2 4 6

El origen de datos

En el ejemplo anterior, como el origen de datos es una matriz, admite implícitamente la interfaz genérica IEnumerable<T>. Este hecho le permite usar una matriz como origen de datos para una consulta LINQ. Los tipos compatibles con IEnumerable<T> o una interfaz derivada, como la interfaz genérica IQueryable<T>, se denominan tipos consultables.

Como tipo consultable de forma implícita, la matriz no requiere ninguna modificación ni tratamiento especial para actuar como origen de datos de LINQ. Lo mismo ocurre con cualquier tipo de colección que admita IEnumerable<T>, incluidas las clases genéricas List<T> y Dictionary<TKey,TValue>, y otras clases de la biblioteca de clases .NET Framework.

Si los datos de origen aún no implementan IEnumerable<T>, se necesita un proveedor LINQ para que implemente la funcionalidad de los operadores de consulta estándar de ese origen de datos. Por ejemplo, LINQ to XML controla el trabajo de carga de un documento XML en un tipo consultable XElement, como se muestra en el ejemplo siguiente. Para más información sobre los operadores de consulta estándar, consulte Información general sobre operadores de consulta estándar (Visual Basic).

' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")

Con LINQ to SQL, primero se crea una asignación relacional de objetos en tiempo de diseño ya sea manualmente o mediante las herramientas de LINQ to SQL de Visual Studio. Se escriben las consultas en los objetos y, en tiempo de ejecución, LINQ to SQL controla la comunicación con la base de datos. En el ejemplo siguiente, customers representa una tabla específica de la base de datos, y Table<TEntity> admite la interfaz genérica IQueryable<T>.

' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)

Para obtener más información sobre cómo crear tipos específicos de orígenes de datos, consulte la documentación de los distintos proveedores de LINQ. (Para obtener una lista de estos proveedores, consulte LINQ [Language Integrated Query]). La regla básica es sencilla: un origen de datos de LINQ es cualquier objeto que admita la interfaz genérica IEnumerable<T> o una interfaz que se haya heredado de ella.

Nota

Los tipos como ArrayList, que admiten la interfaz no genérica IEnumerable, también se pueden usar como orígenes de datos de LINQ. Para obtener un ejemplo en el que se use un objeto ArrayList, consulte Procedimiento para consultar un objeto ArrayList con LINQ (Visual Basic).

Consulta

En la consulta se especifica qué información desea recuperar de los orígenes de datos. También tiene la opción de especificar cómo se debe ordenar, agrupar o estructurar esa información antes de que se devuelva. Para habilitar la creación de consultas, Visual Basic ha incorporado una nueva sintaxis de consulta en el lenguaje.

Cuando se ejecuta, la consulta del ejemplo siguiente devuelve todos los números pares de una matriz de enteros, numbers.

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

La expresión de consulta contiene tres cláusulas: From, Where y Select. La función y el propósito específicos de cada cláusula de expresión de consulta se analiza en Operaciones básicas de consulta (Visual Basic). Para más información, lea Consultas. Tenga en cuenta que, en LINQ, una definición de consulta a menudo se almacena en una variable y se ejecuta más adelante. La variable de consulta, como evensQuery en el ejemplo anterior, debe ser un tipo consultable. El tipo de evensQuery es IEnumerable(Of Integer), asignado por el compilador mediante la inferencia de tipo de variable local.

Es importante recordar que la variable de consulta no efectúa ninguna acción y no devuelve ningún dato. Solo almacena la definición de consulta. En el ejemplo anterior, es el bucle For Each el que ejecuta la consulta.

Ejecución de la consulta

La ejecución de una consulta se realiza de manera independiente con respecto a la creación de una consulta. La creación de consultas define la consulta, pero la ejecución se desencadena mediante un mecanismo diferente. Una consulta se puede ejecutar tan pronto como se define (ejecución inmediata) o se puede almacenar la definición y se puede ejecutar la consulta más adelante (ejecución en diferido).

Ejecución aplazada

Una consulta LINQ típica es similar a la del ejemplo anterior, en la que se define evensQuery. Crea la consulta pero no la ejecuta inmediatamente. En vez de eso, la definición de la consulta se almacena en la variable de consulta evensQuery. La consulta se ejecuta más adelante, normalmente mediante un bucle For Each, que devuelve una secuencia de valores, o mediante la aplicación de un operador de consulta estándar, como Count o Max. Este proceso se conoce como ejecución en diferido.

' Query execution that results in a sequence of values.
For Each number In evensQuery
    Console.Write(number & " ")
Next

' Query execution that results in a single value.
Dim evens = evensQuery.Count()

En el caso de una secuencia de valores, puede acceder a los datos recuperados mediante la variable de iteración del bucle For Each (number en el ejemplo anterior). Dado que la variable de consulta, evensQuery, contiene la definición de consulta en lugar de los resultados de la misma, puede ejecutar una consulta las veces que desee mediante esta variable. Por ejemplo, se puede tener una base de datos que esté siendo actualizada de forma continua por una aplicación independiente. Una vez creada una consulta que recupere los datos de esa base de datos, puede usar un bucle For Each para ejecutar la consulta repetidamente y recuperar los datos más recientes cada vez.

En el ejemplo siguiente se muestra cómo funciona la ejecución diferida. Una vez que se define evensQuery2 y se ejecuta con un bucle For Each, como en los ejemplos anteriores, se cambian algunos elementos del origen de datos numbers. A continuación, un segundo bucle For Each ejecuta evensQuery2 de nuevo. Los resultados son diferentes la segunda vez, ya que el bucle For Each vuelve a ejecutar la consulta con los nuevos valores de numbers.

Dim numberArray() = {0, 1, 2, 3, 4, 5, 6}

Dim evensQuery2 = From num In numberArray
                  Where num Mod 2 = 0
                  Select num

Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8

' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

Salida:

Evens in original array:

0 2 4 6

Evens in changed array:

0 10 2 22 8

Ejecución inmediata

En la ejecución diferida de consultas, la definición de consulta se almacena en una variable de consulta para su posterior ejecución. En la ejecución inmediata, la consulta se ejecuta en el momento de su definición. La ejecución se desencadena cuando se aplica un método que requiere acceso a elementos individuales del resultado de la consulta. La ejecución inmediata a menudo se fuerza mediante uno de los operadores de consulta estándar que devuelven valores únicos. Algunos ejemplos de ello son Count, Max, Average y First. Estos operadores de consulta estándar ejecutan la consulta en cuanto se aplican para calcular y devolver un resultado singleton. Para más información sobre los operadores de consulta estándar que devuelven valores únicos, consulte Operaciones de agregación, Operaciones de elementos y Operaciones cuantificadoras.

La consulta siguiente devuelve un recuento de los números pares de una matriz de enteros. La definición de consulta no se guarda y numEvens es un Integer sencillo.

Dim numEvens = (From num In numbers
                Where num Mod 2 = 0
                Select num).Count()

Puede lograr el mismo resultado mediante el método Aggregate.

Dim numEvensAgg = Aggregate num In numbers
                  Where num Mod 2 = 0
                  Select num
                  Into Count()

También puede forzar la ejecución de una consulta llamando al método ToList o ToArray en una consulta (inmediata) o variable de consulta (diferida), como se indica en el código siguiente.

' Immediate execution.
Dim evensList = (From num In numbers
                 Where num Mod 2 = 0
                 Select num).ToList()

' Deferred execution.
Dim evensQuery3 = From num In numbers
                  Where num Mod 2 = 0
                  Select num
' . . .
Dim evensArray = evensQuery3.ToArray()

En los ejemplos anteriores, evensQuery3 es una variable de consulta, pero evensList es una lista y evensArray es una matriz.

El uso de ToList o ToArray para forzar la ejecución inmediata es especialmente útil en escenarios en los que desea ejecutar la consulta inmediatamente y almacenar en caché los resultados de un único objeto de colección. Para más información acerca de estos métodos, consulte Conversión de tipos de datos.

También puede hacer que una consulta se ejecute mediante un método IEnumerable como, por ejemplo, el método IEnumerable.GetEnumerator.

Consulte también