Introduction aux requêtes LINQ

Mise à jour : novembre 2007

Une requête est une expression qui récupère des données d'une source de données. En général, les requêtes sont exprimées dans un langage de requête spécialisé. Au fil du temps, différents langages ont été développés pour les divers types de sources de données, par exemple, SQL pour les bases de données relationnelles et XQuery pour le XML. Les développeurs ont donc dû apprendre un nouveau langage de requête pour chaque type de source de données ou format de données qu'ils doivent prendre en charge. LINQ simplifie les choses en proposant un modèle cohérent qui permet d'utiliser des données de types de sources et de formats divers. Dans une requête LINQ, vous travaillez toujours avec des objets. Vous utilisez les mêmes modèles de codage de base pour interroger et transformer des données en documents XML, en bases de données SQL, en groupes de données ADO.NET, en collections .NET et en tout autre format pour lesquels un fournisseur LINQ est disponible.

Les trois parties d'une opération de requête

Toutes les opérations de requête LINQ comportent trois actions distinctes :

  1. Obtenir la source de données.

  2. Créer la requête.

  3. Exécuter la requête.

L'exemple suivant montre comment les trois parties d'une opération de requête sont exprimées dans le code source. Il utilise un tableau d'entiers comme source de données pour des raisons pratiques, mais les mêmes concepts sont également applicables à d'autres sources de données. Cet exemple sert de référence dans le reste de cette rubrique.

class IntroToLINQ
{        
    static void Main()
    {
        // The Three Parts of a LINQ Query:
        //  1. Data source.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Query creation.
        // numQuery is an IEnumerable<int>
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;

        // 3. Query execution.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }
    }
}

L'illustration suivante présente la totalité de l'opération de requête. Dans LINQ; l'exécution de la requête est distincte de la requête elle-même. En d'autres termes, vous n'avez pas récupéré de données en créant simplement une variable de requête.

Opération de requête LINQ complète

Source de données

Dans l'exemple précédent, étant donné que la source de données est un tableau, elle prend en charge implicitement l'interface IEnumerable<T> générique. Cela signifie qu'elle peut être interrogée avec LINQ. Une requête est exécutée dans une instruction foreach et foreach requiert IEnumerable ou IEnumerable<T>. Les types qui prennent en charge IEnumerable<T> ou une interface dérivée telle que l'interface générique IQueryable<T> sont des types requêtables.

Un type requêtable ne nécessite aucune modification ni traitement spécial pour servir de source de données LINQ. Si les données sources ne sont pas déjà en mémoire comme type requêtable, le fournisseur LINQ doit les représenter comme telles. Par exemple, LINQ to XML charge un document XML dans un type XElement requêtable :

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Avec LINQ to SQL, vous créez tout d'abord un mappage objet/relationnel au moment du design, soit manuellement, soit à l'aide du Concepteur Objet/Relationnel (Concepteur O/R). Vous écrivez vos requêtes sur les objets et LINQ to SQL gère la communication avec la base de données au moment de l'exécution. Dans l'exemple suivant, Customer représente une table spécifique dans la base de données et Table<Customer> prend en charge un IQueryable<T> générique, qui dérive de IEnumerable<T>.

// Create a data source from a SQL Server database.
// using System.Data.Linq;
DataContext db = new DataContext(@"c:\northwind\northwnd.mdf");

Pour plus d'informations sur la création de types de sources de données spécifiques, consultez la documentation des différents fournisseurs LINQ. Toutefois, la règle de base est très simple : une source de données LINQ correspond à tout type d'objet qui prend en charge l'interface IEnumerable<T> générique ou une interface qui hérite de celle-ci.

Remarque :

Les types tels que ArrayList qui prennent en charge l'interface IEnumerable non générique peuvent également être utilisés comme source de données LINQ. Pour plus d'informations, consultez Comment : interroger un ArrayList avec LINQ.

Requête

La requête spécifie les informations à récupérer de la source ou des sources de données. Elle peut également spécifier la manière dont ces informations doivent être triées, regroupées et mises en forme avant d'être retournées. Une requête est stockée dans une variable de requête et initialisée avec une expression de requête. C# a introduit une nouvelle syntaxe de requête pour simplifier l'écriture des requêtes.

La requête de l'exemple précédent retourne tous les nombres pairs du tableau d'entiers. L'expression de requête contient trois clauses : from, where et select. (Si vous êtes familiarisé avec SQL, vous aurez remarqué que l'ordre des clauses est inversé par rapport à l'ordre dans SQL.) La clause from spécifie la source de données, la clause where applique le filtre et la clause select spécifie le type des éléments retournés. Ces clauses et d'autres clauses de requête sont abordées en détail dans la section Expressions de requête LINQ (Guide de programmation C#). Pour le moment, le point majeur est que dans LINQ, la variable de requête elle-même n'exécute aucune action et ne retourne pas de données. Elle stocke simplement les informations requises pour produire les résultats lors de l'exécution ultérieure de la requête. Pour plus d'informations sur la construction des requêtes en arrière-plan, consultez Vue d'ensemble des opérateurs de requête standard.

Remarque :

Les requêtes peuvent également être exprimées à l'aide de la syntaxe de méthode. Pour plus d'informations, consultez Comparaison de la syntaxe de requête et de la syntaxe de méthode (LINQ).

Exécution de requête

Exécution différée

Comme indiqué précédemment, la variable de requête elle-même stocke simplement les commandes de requête. L'exécution réelle de la requête est différée jusqu'à ce que vous parcouriez la variable de requête dans une instruction foreach. Ce concept, connu sous le nom d' exécution différée, est illustré dans l'exemple suivant :

//  Query execution. 
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

L'instruction foreach constitue également l'emplacement où les résultats de la requête sont récupérés. Par exemple, dans la requête précédente, la variable d'itération num contient chaque valeur (une par une) de la séquence retournée.

Étant donné que la variable de requête elle-même ne contient jamais les résultats de la requête, vous pouvez l'exécuter aussi souvent que vous le souhaitez. Par exemple, vous pouvez mettre à jour une base de données continuellement à l'aide d'une application séparée. Dans votre application, vous pouvez créer une requête qui récupère les données les plus récentes et l'exécuter à plusieurs reprises à un intervalle donné pour récupérer des résultats différents à chaque fois.

Forcer l'exécution immédiate

Les requêtes qui exécutent des fonctions d'agrégation sur une plage d'éléments sources doivent d'abord parcourir ces éléments. Les requêtes Count, Max, Average et First en sont quelques exemples. Elles s'exécutent sans instruction foreach explicite car la requête elle-même doit utiliser foreach pour retourner un résultat. Notez également que ces types de requêtes retournent une valeur unique et non une collection IEnumerable. La requête suivante retourne un décompte des nombres pairs dans le tableau source :

var evenNumQuery = 
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

Pour forcer l'exécution immédiate de toute requête et mettre ses résultats en cache, vous pouvez appeler la méthode ToList<TSource> ou ToArray<TSource>.

List<int> numQuery2 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToArray();

Vous pouvez également forcer l'exécution en plaçant la boucle foreach immédiatement après l'expression de requête. Toutefois, en appelant ToList ou ToArray, vous mettez également en cache toutes les données d'un objet de collection unique.

Voir aussi

Concepts

Vue d'ensemble du Concepteur O/R

Expressions de requête LINQ (Guide de programmation C#)

Référence

foreach, in (Référence C#)

Autres ressources

Mise en route de LINQ en C#

Exemples LINQ

Mots clés de requête (Référence C#)

LINQ and Deferred Execution Video