Écriture de votre première requête LINQ (Visual Basic)

Une requête est une expression qui récupère des données d’une source de données. Les requêtes sont exprimées dans un langage de requête dédié. Au fil du temps, différents langages ont été développés pour différents types de sources de données, par exemple, SQL pour les bases de données relationnelles et XQuery pour XML. Les développeurs d’applications se voient alors dans l’obligation d’apprendre un nouveau langage de requête pour chaque type de source de données ou format de données pris en charge.

LINQ (Language-Integrated Query) simplifie la situation en proposant un modèle cohérent pour travailler avec des données dans différents types de sources et de formats de données. Dans une requête LINQ, vous travaillez toujours avec des objets. Vous utilisez les mêmes schémas de codage de base pour interroger et transformer des données dans des documents XML, bases de données SQL, jeux de données et entités ADO.NET, collections .NET Framework et toute autre source ou tout autre format pour lequel un fournisseur LINQ est disponible. Ce document décrit les trois phases de création et d’utilisation des requêtes LINQ de base.

Les trois phases d’une opération de requête

Les opérations de requête LINQ se résument à trois actions :

  1. Obtenir la ou les sources de données.

  2. Création de la requête

  3. exécutez la requête.

Dans LINQ, l’exécution d’une requête et la création d’une requête sont deux choses différentes. Vous ne récupérez pas de données simplement en créant une requête. Ce point est abordé en détail plus loin dans cette rubrique.

L’exemple suivant illustre les trois parties d’une opération de requête. L’exemple utilise un tableau d’entiers comme source de données pratique à des fins de démonstration. Toutefois, les mêmes concepts s’appliquent également à d’autres sources de données.

Notes

Dans la page Compiler, Concepteur de projets (Visual Basic), vérifiez que Option infer est définie sur Activé.

' 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

Sortie :

0 2 4 6

Source de données

Comme la source de données de l’exemple précédent est un tableau, elle prend en charge implicitement l’interface générique IEnumerable<T>. C’est ce qui vous permet d’utiliser un tableau comme source de données pour une requête LINQ. Les types qui prennent en charge IEnumerable<T> ou une interface dérivée, comme l’interface générique IQueryable<T>, sont appelés des types requêtables.

En tant que type implicitement requêtable, le tableau ne demande aucune modification ni traitement spécial pour servir de source de données LINQ. Il en va de même pour tout type de collection prenant en charge IEnumerable<T>, notamment les classes génériques List<T>, Dictionary<TKey,TValue> et autres classes de la bibliothèque de classes .NET Framework.

Si les données sources n’implémentent pas déjà IEnumerable<T>, un fournisseur LINQ est nécessaire pour implémenter les fonctionnalités des opérateurs de requête standard pour cette source de données. Par exemple, LINQ to XML gère le travail de chargement d’un document XML dans un type XElement requêtable, comme illustré dans l’exemple suivant. Pour plus d’informations sur les opérateurs de requête standard, consultez Vue d’ensemble des opérateurs de requête standard (Visual Basic).

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

Avec LINQ to SQL, vous commencez par créer un mappage O/R au moment du design, manuellement ou à l’aide des Outils LINQ to SQL dans Visual Studio. Vous écrivez vos requêtes sur les objets, et à l’exécution ; LINQ to SQL gère la communication avec la base de données. Dans l’exemple suivant, customers représente une table spécifique de la base de données et Table<TEntity> prend en charge l’interface générique 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)

Pour plus d’informations sur la création de types spécifiques de sources de données, consultez la documentation relative aux différents fournisseurs LINQ. (Pour obtenir la liste de ces fournisseurs, consultez LINQ (Language-Integrated Query).) La règle de base est simple : une source de données LINQ est un objet qui prend en charge l’interface générique IEnumerable<T> ou une interface qui hérite de celui-ci.

Notes

Les types comme ArrayList qui prennent en charge l’interface non générique IEnumerable peuvent également être utilisés comme sources de données LINQ. Pour obtenir un exemple qui utilise une classe ArrayList, consultez Guide pratique : interroger une ArrayList avec LINQ (Visual Basic).

La requête

Dans la requête, vous spécifiez les informations que vous voulez récupérer à partir de la ou des sources de données. Vous avez également la possibilité de spécifier la façon dont ces informations doivent être triées, groupées ou structurées avant de les retourner. Pour activer la création de requêtes, Visual Basic a incorporé une nouvelle syntaxe de requête dans le langage.

Lorsqu’elle est exécutée, la requête de l’exemple suivant retourne tous les nombres pairs d’un tableau d’entiers, 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

L’expression de requête contient trois clauses : From, Where et Select. La fonction et l’objectif spécifiques de chaque clause d’expression de requête sont abordés dans Opérations de requête de base (Visual Basic). Pour plus d’informations, consultez Requêtes. Notez que dans LINQ, une définition de requête est souvent stockée dans une variable et exécutée ultérieurement. La variable de requête, comme evensQuery dans l’exemple précédent, doit être un type requêtable. Le type de evensQuery est IEnumerable(Of Integer), attribué par le compilateur à l’aide de l’inférence de type local.

Il est important de retenir que la variable de requête elle-même n’effectue aucune action et ne retourne aucune donnée. Elle stocke uniquement la définition de requête. Dans l’exemple précédent, c’est la boucle For Each qui exécute la requête.

Exécution des requêtes

L’exécution d’une requête et la création d’une requête sont deux choses différentes. La création de requête définit la requête, tandis que l’exécution est déclenchée par un mécanisme différent. Une requête peut être exécutée dès qu’elle est définie (exécution immédiate) ou la définition peut être stockée et la requête peut être exécutée ultérieurement (exécution différée).

Exécution différée

Une requête LINQ ressemble typiquement à celle de l’exemple précédent, où evensQuery est défini. Il crée la requête, mais ne l’exécute pas immédiatement. À la place, la définition de la requête est stockée dans la variable de requête evensQuery. Vous exécutez la requête ultérieurement, généralement en utilisant une boucle For Each qui retourne une séquence de valeurs, ou en appliquant un opérateur de requête standard comme Count ou Max. Ce processus est connu sous le nom d’exécution différée.

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

Pour une séquence de valeurs, vous accédez aux données récupérées à l’aide de la variable d’itération dans la boucle For Each (number dans l’exemple précédent). Étant donné que la variable de requête evensQuery contient la définition de requête plutôt que les résultats de la requête, vous pouvez exécuter une requête aussi souvent que vous le souhaitez en utilisant la variable de requête plusieurs fois. Par exemple, vous avez peut-être une base de données dans votre application qui est constamment mise à jour par une application distincte. Une fois que vous avez créé une requête qui récupère des données de cette base de données, vous pouvez utiliser une boucle For Each pour exécuter la requête à plusieurs reprises, en récupérant à chaque fois les données les plus récentes.

L’exemple suivant montre comment fonctionne l’exécution différée. Une fois evensQuery2 défini et exécuté avec une boucle For Each, comme dans les exemples précédents, certains éléments de la source de données numbers sont modifiés. Ensuite, une deuxième boucle For Each réexécute evensQuery2. Les résultats sont différents la deuxième fois parce que la boucle For Each réexécute la requête en utilisant les nouvelles valeurs dans 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()

Sortie :

Evens in original array:

0 2 4 6

Evens in changed array:

0 10 2 22 8

Exécution immédiate

Dans l’exécution différée des requêtes, la définition de requête est stockée dans une variable de requête pour être exécutée ultérieurement. Dans l’exécution immédiate, la requête est exécutée lors de sa définition. L’exécution est déclenchée lorsque vous appliquez une méthode qui nécessite l’accès à des éléments individuels du résultat de la requête. L’exécution immédiate est souvent forcée à l’aide de l’un des opérateurs de requête standard qui retournent des valeurs uniques. Par exemple : Count, Max, Average et First. Ces opérateurs de requête standard exécutent la requête dès qu’ils sont appliqués afin de calculer et de retourner un résultat singleton. Pour plus d’informations sur les opérateurs de requête standard qui retournent des valeurs uniques, consultez Opérations d’agrégation, Opérations d’élément et Opérations de quantificateur.

La requête suivante retourne un nombre de chiffres pairs du tableau d’entiers. La définition de requête n’est pas enregistrée et numEvens est un simple Integer.

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

Vous pouvez obtenir le même résultat en utilisant la méthode Aggregate.

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

Vous pouvez également forcer l’exécution d’une requête en appelant la méthode ToList ou ToArray sur une requête (immédiate) ou une variable de requête (différée), comme indiqué dans le code suivant.

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

Dans les exemples précédents, evensQuery3 est une variable de requête, mais evensList est une liste et evensArray est un tableau.

L’utilisation de ToList ou de ToArray pour forcer l’exécution immédiate est particulièrement utile dans les scénarios où vous souhaitez exécuter la requête immédiatement et mettre en cache les résultats dans un seul objet de collection. Pour plus d’informations sur ces méthodes, consultez Conversion des types de données.

Vous pouvez également provoquer l’exécution d’une requête en utilisant une méthode IEnumerable telle que la méthode IEnumerable.GetEnumerator.

Voir aussi