Escribir la primera consulta con LINQ (Visual Basic)

Actualización: noviembre 2007

Una consulta es una expresión que recupera datos de un origen de datos. Las consultas se expresan en un lenguaje de consultas dedicado. A lo largo del tiempo se han ido desarrollando lenguajes diferentes para los distintos tipos de orígenes de datos, como SQL para las bases de datos relacionales y XQuery para XML. De esta manera, el desarrollador de aplicaciones debe aprender un nuevo lenguaje de consultas para cada tipo de origen de datos o formato de datos admitido.

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

Las tres etapas de una operación de consulta

Las operaciones de consulta LINQ se componen de tres acciones:

  1. Obtención de uno o varios orígenes de datos.

  2. Creación de la consulta.

  3. Ejecución de la consulta.

En LINQ, la ejecución y la creación de una consulta son operaciones distintas. Por el simple hecho de crear una consulta, no se recuperan datos. Este punto se analiza con más detalle más adelante, en este mismo tema.

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

' 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

Resultado:

0 2 4 6

El origen de datos

Dado que el origen de datos del ejemplo anterior es una matriz, se admite implícitamente la interfaz genérica IEnumerable<T>. Es este hecho lo que permite utilizar una matriz como origen de datos para una consulta LINQ. Los tipos que admiten IEnumerable(Of T) o una interfaz derivada, como la genérica IQueryable<T>, se conocen como tipos que se pueden consultar.

Como tipo que se puede consultar implícitamente, la matriz no requiere modificaciones ni un tratamiento especial para actuar como origen de datos LINQ. Lo mismo sucede con cualquier tipo de colección que admita IEnumerable(Of T), incluidas las genéricas List<T>, Dictionary<TKey, TValue> y otras clases de la biblioteca de clases de .NET Framework.

Si los datos de origen todavía no implementan IEnumerable(Of T), se requiere un proveedor LINQ para implementar la funcionalidad de los operadores de consulta estándar para ese origen de datos. Por ejemplo, LINQ to XML controla el trabajo de cargar un documento XML en un tipo XElement que se pueda consultar, como se muestra en el ejemplo siguiente. Para obtener más información sobre los operadores de consulta estándar, vea Información general sobre operadores de consulta estándar.

' Create a data source from an XML document.
Dim contacts As XElement = 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 el Diseñador relacional de objetos (Diseñador R/O). Después, 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 concreta 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 de orígenes de datos específicos, consulte la documentación de los distintos proveedores LINQ. (Para obtener una lista de estos proveedores, vea Language-Integrated Query (LINQ).) La regla básica es simple: un origen de datos LINQ es cualquier objeto que admite la interfaz genérica IEnumerable<T> o una interfaz que herede de ella.

Nota:

También se pueden utilizar tipos como ArrayList, que admite la interfaz no genérica IEnumerable, como orígenes de datos LINQ. Para obtener un ejemplo donde se usa ArrayList, vea Cómo: Consultar un objeto ArrayList con LINQ.

La consulta

En la consulta se especifica qué información se desea recuperar del origen o de los orígenes de datos. También tiene la opción de especificar cómo se debería ordenar, agrupar o estructurar esa información antes de devolverse. Para habilitar la creación de consultas, Visual Basic incorpora 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 una de las cláusulas de las expresiones de consulta se analiza en Operaciones básicas de consulta (Visual Basic). Para obtener más información, consulte Consultas (Visual Basic). Observe que en LINQ una definición de consulta suele almacenarse en una variable y se ejecuta después. La variable de consulta, como evensQuery en el ejemplo anterior, debe ser un tipo que se pueda consultar. 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 propia variable de consulta no realiza ninguna acción ni devuelve datos. Sólo almacena la definición de la consulta. En el ejemplo anterior, es el bucle For Each el que ejecuta la consulta.

Ejecución de la consulta

La ejecución de la consulta es proceso independiente de su creación. La creación de la consulta la define, pero su ejecución la desencadena un mecanismo diferente. Se puede ejecutar un consulta en cuanto esté definida (ejecución inmediata), o se puede guardar la definición y ejecutar la consulta más tarde (ejecución diferida).

Ejecución diferida

Una consulta LINQ típica se parece a la del ejemplo anterior, en el que se define evensQuery. En él se crea la consulta, pero no se ejecuta de inmediato. 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 aplicando un operador de consulta estándar, como Count o Max. Este proceso se denomina ejecución diferida.

' 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()

Para una secuencia de valores, se tiene acceso 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 la consulta en lugar de los resultados, puede ejecutar una consulta tantas veces como desee, utilizando la variable de consulta. Por ejemplo, podría tener una base de datos en su aplicación que sea actualizada continuamente por una aplicación independiente. Después de haber creado una consulta que recupere los datos de esa base de datos, puede utilizar un bucle For Each para ejecutar la consulta una y otra vez, recuperando en cada ocasión los datos más recientes.

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

Dim numberArray() As Integer = {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()

Resultado:

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 la 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 activa al aplicar un método que requiere acceso a los elementos individuales del resultado de la consulta. La ejecución inmediata se fuerza mediante el uso de uno de los operadores de consulta estándar que devuelven valores únicos. Algunos ejemplos 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 obtener más información sobre los operadores de consulta estándar que devuelven valores únicos, vea 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 la consulta no se guarda y numEvens es un Integer simple.

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

Se puede conseguir el mismo resultado utilizando 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 (ejecución inmediata) o variable de consulta (ejecución diferida), como se muestra 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 = evensQuery.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 cuando se desea ejecutar la consulta inmediatamente y almacenar los resultados en memoria caché en un objeto de colección único. Para obtener más información sobre estos métodos, vea Convertir tipos de datos.

También puede provocar la ejecución de una consulta mediante el uso de un método IEnumerable, como GetEnumerator (Método, objeto Collection).

Vea también

Tareas

Ejemplos de consultas (Visual Basic)

Conceptos

Información general sobre el Diseñador relacional de objetos

Inferencia de tipo de variable local

Información general sobre operadores de consulta estándar

Introducción a LINQ en Visual Basic

Otros recursos

Introducción a LINQ en Visual Basic

Ejemplos de LINQ

LINQ en Visual Basic

Consultas (Visual Basic)