Chapitre 1 Introduction à LINQ En surfant sur le Web, vous trouverez différentes descriptions de LINQ (Language Integrated Query), et parmi elles : LINQ est un modèle de programmation uniforme de n importe quel type de données. LINQ vous permet de récupérer des données et de les manipuler à l aide d un modèle cohérent qui est indépendant des sources de données. LINQ est un autre outil d incorporation de requêtes SQL (Structured Query Language) dans du code. LINQ représente une couche d abstraction inédite des données. Toutes ces descriptions sont correctes jusqu à un certain point, car chacune se focalise uniquement sur un aspect particulier de LINQ. LINQ peut faire beaucoup plus qu incorporer des requêtes SQL. Il est beaucoup plus facile à utiliser qu un «modèle de programmation uniforme» et c est bien autre chose qu un simple ensemble de règles de modélisation de données. Qu est-ce que LINQ? LINQ est un modèle de programmation qui introduit des requêtes, comme concept principal, dans n importe quel langage Microsoft.NET. Cependant, le support complet de LINQ impose des extensions au langage que vous utilisez. Ces extensions augmentent la productivité du développeur en offrant une syntaxe plus compacte, plus significative et expressive pour manipuler des données. Plus d infos Des détails sur les extensions du langage se trouvent à l annexe B, C# 3.0 : nouvelles caractéristiques du langage et à l annexe C, Visual Basic 2008 : nouvelles caractéristiques du langage. LINQ fournit une méthodologie qui simplifie et unifie l implémentation de n importe quel type d accès aux données. Il ne vous oblige pas à utiliser une architecture spécifique ; il facilite l implémentation de plusieurs architectures existantes pour accéder aux données, par exemple : RAD/prototype Client/serveur N-tier Client intelligent LINQ a fait sa première apparition en septembre 2005 en présentation technique préléminaire. Depuis lors, il a évolué du statut d extension de Microsoft Visual Studio 2005 3
4 Partie I Fondements de LINQ vers le statut de partie intégrante de.net Framework 3.5 et de Visual Studio 2008, tous deux sortis en novembre 2007. La première version de LINQ prend en charge directement plusieurs sources de données. Elle ne comporte pas LINQ to Entities, qui sortira avec ADO.NET Entity Framework en 2008. Dans ce livre, nous décrivons les implémentations de LINQ actuelles et à venir, utilisées pour accéder à différentes sources de données : LINQ to Objects LINQ to ADO.NET LINQ to SQL LINQ to DataSet LINQ to Entities (voir note ci-dessous) LINQ to XML Important Dans ce livre, nous décrivons LINQ to Entities en nous basant sur du code beta. Nous avons essayé de mettre à jour le contenu en tenant compte des spécifications finales. De plus, nous fournirons des informations, des corrections du livre et nous mettrons à jour les exemples de code à l adresse : http://www.programminglinq.com. Extensions de LINQ LINQ peut être étendu pour prendre en charge d autres sources de données. Les extensions possibles peuvent être LINQ to SharePoint, LINQ to Exchange et LINQ to LDAP, pour n en citer que quelques unes. En fait, certaines implémentations possibles sont déjà disponibles avec LINQ to Objects. Nous décrivons dans ce chapitre une possible requête LINQ to Reflection à la section LINQ to Objects. Des extensions plus avancées de LINQ sont exposées au chapitre 12, Extensions de LINQ. Certaines des implémentations de LINQ sont illustrées au chapitre 14, Autres implémentations de LINQ. LINQ est susceptible d avoir un impact sur la manière dont les applications sont codées. Il serait incorrect de penser que LINQ modifie l architecture des applications car son but est d offrir un ensemble d outils qui améliorent l implémentation du code en l adaptant à plusieurs architectures différentes. Cependant, nous espérons que LINQ affectera des parties critiques des couches d une solution multi niveaux. Par exemple, nous envisageons l utilisation de LINQ dans une procédure SQLCLR stockée, avec un transfert direct de l expression de la requête au moteur SQL au lieu d utiliser une instruction SQL. Plusieurs évolutions possibles pourraient émaner de LINQ, mais nous ne devons pas oublier que SQL est un standard largement accepté qui ne peut être remplacé facilement par un autre, juste pour des raisons de performances. Néanmoins, LINQ est une étape intéressante de l évolution des langages de programmation de pointe. La nature déclarative de sa syntaxe
Chapitre 1 Introduction à LINQ 5 peut être intéressante pour des utilisations autres que l accès aux données, telles que la programmation parallèle offerte par Parallel LINQ (PLINQ). D autres services peuvent être offerts par un environnement d exécution à un programme écrit utilisant un niveau supérieur d abstraction tel que celui offert par LINQ. Il est important aujourd hui, et peutêtre fondamental demain, d avoir une bonne compréhension de cette nouvelle technologie. Plus d infos Parallel LINQ (PLINQ) est traité au chapitre 13, Parallel LINQ. Pourquoi avons-nous besoin de LINQ? Aujourd hui, des données gérées par un programme peuvent provenir de différentes sources : un tableau, un graphe d objets, un document XML, une base de données, un fichier texte, une clé du registre, un message de courrier électronique, un contenu de message SOAP (Simple Object Access Protocol ), un fichier Excel de Microsoft Office. La liste est longue. Chaque source de données a son propre modèle d accès. Lorsque vous devez interroger une base de données, vous utilisez en général SQL. Vous naviguez dans les données XML à l aide du DOM (Document Object Model) ou de XPath/XQuery. Vous procédez par itérations dans un tableau et vous construisez des algorithmes pour naviguer dans un graphe d objets. Vous utilisez des interfaces spécifiques de programmation d applications (API) pour accéder à d autres sources de données, telles qu un fichier Excel, un message e-mail ou la base de registre de Windows. Enfin, vous utilisez différents modèles de programmation pour accéder à différentes sources de données. L unification des techniques d accès aux données dans un seul modèle d ensemble a été tentée de différentes manières. Par exemple, les fournisseurs ODBC (Open Database Connectivity) vous permettent d interroger un fichier Excel comme vous le feriez pour un dépôt WMI (Windows Management Instrumentation). Avec ODBC, vous utilisez un langage de type SQL pour accéder à des données représentées par l intermédiaire d un modèle relationnel. Cependant, parfois, des données sont représentées plus efficacement par un modèle hiérarchique ou par un modèle en réseau que par un modèle relationnel. De plus, si un modèle de données n est pas lié à un langage spécifique, vous devrez probablement gérer différents systèmes de types. Toutes ces différences créent un «défaut d adaptation d impédance» (comme disent les électriciens) entre les données et le code. LINQ corrige ces problèmes en offrant un moyen uniforme d accéder aux données et de les gérer sans forcer l adoption d un modèle unique «toutes tailles». LINQ fait usage de capacités communes dans les opérations de différents modèles de données au lieu d écraser les différentes structures sous-jacentes. En d autres termes, en utilisant LINQ vous conservez des structures de données existantes hétérogènes, telles que les classes ou les tables, mais vous obtenez une syntaxe uniforme pour effectuer les requêtes de tous les types de données,
6 Partie I Fondements de LINQ indépendamment de leur présentation physique. Pensez aux différences entre un graphe d objets en mémoire et des tables relationnelles avec des relations pertinentes. Avec LINQ, vous pouvez utiliser la même syntaxe de requête pour les deux modèles. Voici une simple requête LINQ concernant une solution logicielle qui retourne les noms de clients en Italie. (Ne vous préoccupez pas pour l instant de la syntaxe et des mots-clés tels que var.) select c.companyname; Le résultat de cette requête est une liste de chaînes. Vous pouvez énumérer ces valeurs à l aide d une boucle foreach en C# : foreach ( string name in query ) { Console.WriteLine( name ); } La définition de requête query et la boucle foreach sont toutes deux des instructions habituelles en C# 3.0 ; mais que représente l expression Customers? À ce stade, vous pourriez vous demander quel est l objet de la requête. Cette requête est-elle une nouvelle forme de Embedded SQL? Pas du tout. Vous pouvez appliquer la même requête (et la boucle foreach) à une base de données SQL, à un objet DataSet, à un tableau d objets en mémoire, à un service distant ou à de nombreuses autres variétés de données. Customers pourrait être une collection d objets, par exemple : Customer[] Customers; Customers pourrait être un DataTable dans un DataSet : DataSet ds = GetDataSet(); DataTable Customers = ds.tables["customers"]; Customers pourrait être aussi une classe d entités décrivant une table physique d une base de données relationnelle : DataContext db = new DataContext( ConnectionString ); Table<Customer> Customers = db.gettable<customer>(); Ou bien, Customers pourrait être une classe d entités, décrivant un modèle conceptuel, mappée à une base de données relationnelle : NorthwindModel datamodel = new NorthwindModel(); ObjectQuery<Customer> Customers = datamodel.customers; Fonctionnement de LINQ Comme vous l apprendrez au chapitre 2, Fondements de la syntaxe de LINQ, la syntaxe de type SQL utilisée dans LINQ est appelée expression de requête. Une requête de type SQL mélangée avec la syntaxe d un programme écrit dans un langage qui n est pas SQL est
Chapitre 1 Introduction à LINQ 7 généralement appelée Embedded SQL, mais les langages qui l implémentent, le font en utilisant une syntaxe simplifiée. Dans Embedded SQL, ces instructions ne sont pas intégrées dans la syntaxe native du langage et dans le système de types parce qu elles ont différentes syntaxes et plusieurs restrictions relatives à leur interaction. Par ailleurs, LINQ n est pas limité à l interrogation des bases de données, comme l est Embedded SQL. Il offre beaucoup plus que Embedded SQL une syntaxe de requête qui est intégrée dans un langage. Mais alors, comment fonctionne LINQ? Lorsque vous écrivez le code suivant en utilisant LINQ Customer[] Customers = GetCustomers(); select c; le compilateur génère ce code : Customer[] Customers = GetCustomers(); IEnumerable<Customer> query = Customers.Where( c => c.country == "Italy" ); Lorsque la requête devient plus complexe, comme vous pouvez le voir ici (à partir de maintenant, nous laisserons de côté la déclaration de Customers pour des impératifs de concision) : orderby c.name select new { c.name, c.city }; le code généré est également plus complexe : Customers.Where( c => c.country == "Italy" );.OrderBy( c => c.name ).Select( c => new { c.name, c.city } ); Comme vous le voyez, le code appelle apparemment des membres d instances sur l objet retourné depuis l appel précédent : Where est appelée sur Customers, OrderBy est appelée sur l objet retourné par Where et finalement Select est appelée sur l objet retourné par OrderBy. Vous verrez que ce comportement est régulé par ce qu on appelle des méthodes d extension dans le langage d accueil (C# dans ce cas). L implémentation des méthodes Where, OrderBy et Select appelées par la requête exemple dépendent du type de Customers et des espaces de noms spécifiés par les instructions using appropriées. Les méthodes d extension sont une caractéristique fondamentale de la syntaxe utilisée par LINQ pour pouvoir coopérer avec les différentes sources de données en utilisant une même syntaxe