Vous êtes sur la page 1sur 36

Site : www.e-naxos.com Blog : www.e-naxos.com/blog Formation C# Visual Studio - Delphi.

.NET/Win32 Audit, Conseil, Dveloppement Copyright 2007 Olivier DAHAN Version 1.1 13-12-2007 Reproduction, utilisation et diffusion interdites sans lautorisation de lauteur. Pour plus dinformation contacter odahan@e-naxos.com

IMPORTANT : Sur certains sites le texte qui suit peut prsenter une mise en page plus ou moins adapte (par exemple sil est transform en html). Noubliez pas que vous pouvez toujours tlcharger larticle original au format PDF accompagn du code source des exemples sur le site enaxos, rubrique tlchargements, groupe articles c# .

Pr-requis : Cet article suppose que vous ayez pris connaissance des nouveauts syntaxiques de C# 3.0. Si cela nest pas le cas je vous suggre la lecture de mon article prcdent les nouveauts syntaxiques de C# 3.0 article tlchargeable sur mon site comme le prsent. De plus plusieurs billets de mon blog propose des informations complmentaires sur LINQ, nhsitez pas les lire pour mieux cerner le sujet.

Note de la version 1.1 : Faire uvre de vulgarisation nest jamais chose aise, vouloir simplifier pour mieux faire comprendre on utilise par force quelques approximations. Dans la version 1.0 de cet article je traitais Linq to Entities mais en montrant Linq to SQL pour simplifier la dmonstration. Mais sans en prvenir le lecteur cette approche avait linconvnient dventuellement brouiller un peu les ides de ce dernier entre Linq to SQL et Linq to Entities et leurs diagrammes respectifs. La version 1.1 tente de clarifier les diffrences entre ces deux types de modlisation. Les lecteurs y trouveront certainement mieux leur compte, en tout cas je lespre.

Page 1

Ce qui fait de LINQ une nouveaut ........................................................................................................................................................................3 LINQ to Objects .......................................................................................................................................................................................................... 4 Construction de la requte ................................................................................................................................................................................... 5 LINQ to ADO.NET ....................................................................................................................................................................................................... 7 LINQ to ADO.NET, cest quoi ? ..........................................................................................................................................................................8 LINQ to SQL .....................................................................................................................................................................................................8 LINQ to Dataset ................................................................................................................................................................................................8 LINQ to Entities ................................................................................................................................................................................................8 LINQ to XML .................................................................................................................................................................................................................8 Les exemples ................................................................................................................................................................................................................8 LINQ to SQL ...........................................................................................................................................................................................................8 Les bases du mapping .................................................................................................................................................................................. 10 Un contexte dexcution ..................................................................................................................................................................................11 Premire requte ............................................................................................................................................................................................ 12 Variante............................................................................................................................................................................................................. 13 Compliquons un peu la requte ........................................................................................................................................................................ 14 Filtrage .............................................................................................................................................................................................................. 14 Tri........................................................................................................................................................................................................................15 Autre syntaxe................................................................................................................................................................................................... 16 Concluons sur LINQ to SQL ......................................................................................................................................................................... 17 LINQ to Dataset..................................................................................................................................................................................................... 17 Exemple .............................................................................................................................................................................................................17 LINQ to XML ......................................................................................................................................................................................................... 19 Crer des donnes XML ............................................................................................................................................................................... 19 Marier LINQ to XML avec LINQ to SQL/Dataset/Entities .................................................................................................................... 21 LINQ to Entities ................................................................................................................................................................................................... 23 Dveloppeur != Plombier ..............................................................................................................................................................................24 Changer de dimension .................................................................................................................................................................................24 Passons la ralit ................................................................................................................................................................................... 25 Construire le modle ......................................................................................................................................................................................27 Personnaliser le modle ............................................................................................................................................................................... 30 Premier test du modle ................................................................................................................................................................................. 31 Ajout dune procdure stocke au modle ............................................................................................................................................... 32 Utiliser les relations ....................................................................................................................................................................................... 33 Crer des relations ........................................................................................................................................................................................34 Modifications des donnes........................................................................................................................................................................... 35 Conclusion .................................................................................................................................................................................................................. 36

Table des matires

LINQ
Language-Integrated Query, ou Requtage integr au langage, est un des ajouts les plus marquants du Framework 3.5 et de C# 3.0. Et les mots sont pess. Trs raisonnablement, lavance conceptuelle derrire LINQ est un bouleversement norme au moins aussi important que le Framework .NET lui-mme sa sortie avec toutes ses technologies et ses outils. Vous aidez prendre la mesure de ce bouleversement, cest le but du prsent article. Une prcision : Ce qui va suivre nest pas un cours sur LINQ, plus humblement cest un vaste tour dhorizon de cette technologie, la fois par des explications sur le pourquoi et le comment et par des exemples de code. Je reviendrai dans dautres articles plus en dtail sur certains aspects. Pour linstant, venez dcouvrir pourquoi il y aura un avant LINQ et un aprs LINQ et pourquoi vous refuserez de dvelopper dans le futur avec des langages ne le supporteront pas

Page 2

Ce qui fait de LINQ une nouveaut


Si vous avez une vague ide de ce quest LINQ vous pensez peut-tre que LINQ nest finalement quun nouveau procd dO/R mapping (ORM) comme il en existe de longue date sous .NET ou sous Java, et mme sous Delphi avec ECO. Cette approche est fausse plus dun titre ! Tout dabord LINQ nest pas un utilitaire, un framework lourd matriser, cest une volution naturelle de la syntaxe de C#, il fait donc partie intgrante du langage (et de la plateforme .NET qui fournit les services). Ce nest ni un ajout, ni une verrue, ni une astuce, cest C# 3.0, tout simplement. Ensuite, LINQ nest en rien un simple outil dORM, cest plutt un SQL totalement objet qui sadapte diffrents contextes, dont les bases de donnes mais pas seulement. Car mme cette comparaison est rductrice, LINQ est bien plus simple et plus puissant par exemple que lOCL utilis par ECO de Borland ou que les diffrentes versions de Object-SQL implmentes dans certains SGBD-Orient Objet comme OQL dans O2 par exemple. Mme NHibernate prpare un NHibernate to LINQ malgr le succs de loutil et de sa philosophie. Quant DB4O, elle propose quelque chose de plus utilisable, do son relatif succs en ce moment mais tout comme la base MySQL la pub parle de gratuit, mais en ralit les licences pros sont onreuses Gardez vos sous ! Tout cela cote plus cher quun indispensable abonnement MSDN qui vous offre Visual Studio (et bien plus) avec son LINQ intgr ! LINQ est donc trs diffrent de tout cela. Pourquoi ? Parce que tous ces langages plus ou moins intgrs des EDI, des frameworks ou certains SGBD-O sont totalement disjoints du langage de programmation principal utilis par le dveloppeur, ils ont une syntaxe propre et non cohrente avec celle du langage des applications complexifiant la conception de ces dernires et forant une gymnastique intellectuelle la charge du dveloppeur pour faire le lien entre les deux mondes On change donc un mapping O/R pour un mapping encore plus complexe entre deux systmes objets incompatibles ! LINQ vite tous ces problmes, LINQ cest du C# (ou du VB), ni plus ni moins. LINQ est une aussi une continuit logique vers plus dobjectivation des donnes, mouvement entam depuis longtemps sous .NET. On se rappellera par exemple de lintroduction dans le Framework 2.0 de lObjectDatasource permettant dutiliser une grappe dobjets en mmoire comme source de donnes pour des objets dinterface sous ASP.NET, ou mme du DataBinding de .NET 1.0 qui fait que tout objet avec ses proprits peut tre vu comme une source de donnes. Ces progrs taient immenses (comparez le DataBinding de .NET 1.0 avec la complexit de crer un composant li une source de donnes sous VC++ et la MFC, Delphi ou VB Win32 par exemple) mais on pouvait aller plus loin. LINQ est, de fait, laboutissement de ce long voyage vers plus dabstraction et en mme temps vers plus de pragmatisme. LINQ est tout sauf compliqu, il est simple et naturel. Mais ce quil permet de faire est dune incroyable puissance. Pourquoi LINQ ? Simplement parce quun logiciel passe son temps manipuler des donnes (au sens large) et quil tait logique quun jour les langages informatiques intgrent enfin

Page 3

ces dernires leur fonctionnement. Faire de sources XML ou SQL, et mme de listes dobjets, des citoyens part entire du langage, cest a LINQ : arrter la schizophrnie entre donnes du langage (entiers, chanes, collections) et donnes tout court (le plus souvent persistantes sur un SGBD mais pas seulement) en unifiant la syntaxe de manipulation quelle que soit la source. LINQ est ainsi utilisable dans plusieurs contextes dont le dveloppeur na plus se soucier puisque LINQ permet de les manipuler de la mme faon, comme des objets avec une mme syntaxe. Et cest en cela que LINQ est une relle innovation. Au mme titre que le Framework lui-mme permet avec un mme EDI, un mme langage, une mme librairie, de concevoir aussi bien des applications Windows que des applications Web ou des applications pour Smartphone, LINQ avec la mme syntaxe permet de trier, de filtrer et de mettre jour des donnes quil sagisse dobjets mtiers, de fichiers XML ou de sources SQL. LINQ se dcline en plusieurs armes partageant tous une syntaxe de base et lesprit de LINQ. Mais chacun est spcialis pour un contexte donn. Pour comprendre la justification et le fonctionnement de ces diffrentes adaptations de LINQ, suivez le guide

LINQ to Objects
LINQ to Objects pourrait tre vu comme le socle de base. Il a pour but de permettre linterrogation et la manipulation de collections dobjets. Ces dernires pouvant avoir des relations hirarchiques avec dautres collections et ainsi former un graphe. Par essence LINQ est donc relationnel, mais pas au sens des bases de donnes, au sens dun langage objet. Les bases de donnes SQL sont fondes sur la notion de tableaux appels tables, c'est--dire des surfaces rectangulaires accessibles en ligne / colonne. Tout est table sous SQL, et un rsultat de requte est aussi une table. Cest l toute la puissance de SQL mais cest aussi sa faiblesse : il ne peut pas retourner un graphe, juste un rectangle avec des cases. LINQ travaille sur des graphes et sait retourner des graphes. SQL ne sait tout simplement pas le faire. Dailleurs LINQ to Objects ne se limite pas la manipulation des objets crs par le dveloppeur, il permet dinterroger nimporte quelles donnes, mme celles retournes par le systme du moment quil sagit dune collection. Pour sen convaincre et apprhender tout de suite lintrt vident de LINQ prenons un exemple trs simple (peu importe pour le moment que la syntaxe vous chappe peut-tre) qui consiste lister les fichiers du rpertoire temporaire de Windows.

Page 4

public static void LanceDemo() { string temp = Path.GetTempPath(); DirectoryInfo info = new DirectoryInfo(temp); var query = from f in info.GetFiles() where f.Length > 1024 * 10 orderby f.Length descending select new { f.Length, f.LastWriteTime, f.Name }; ObjectDumper.Write(query); Console.ReadLine(); }

La sortie la console, lorsquon appelle la mthode LanceDemo() est la suivante :


Length=57689462 Length=56904858 Length=19757712 Length=18874368 Length=7340032 Length=5891268 Length=5882250 Length=11436 Length=11436 Length=11378 Length=10996 LastWriteTime=20/11/2007 Name=VSMsiLog26AC.txt LastWriteTime=06/08/2007 Name=VSMsiLog6E4B.txt LastWriteTime=19/11/2007 Name=VSMsiLog3BA0.txt LastWriteTime=06/08/2007 Name=WinSAT_DX.etl LastWriteTime=06/08/2007 Name=WinSAT_KernelLog.etl LastWriteTime=20/11/2007 Name=dd_WMPPC_5_0_MSI31C5.txt LastWriteTime=06/08/2007 Name=dd_WMPPC_5_0_MSI772C.txt LastWriteTime=07/08/2007 LastWriteTime=06/08/2007 LastWriteTime=13/11/2007 LastWriteTime=08/11/2007 Name=SilverlightUI403E.txt Name=SilverlightUI77FC.txt Name=SilverlightUI11EE.txt Name=vs_setup.pdi

Lorsque la mthode LanceDemo() est invoque, elle affiche la console tous les fichiers du chemin temporaire de lOS en ne prenant que ceux dont la taille est suprieure 10 Ko, le tout en prsentant les fichiers par ordre dcroissant de taille. Seule trois informations par fichier sont retournes : sa longueur, sa date de dernire criture et son nom. tesvous capable de faire un tel traitement aussi clairement et en si peu de lignes sans LINQ ? Vous connaissez la rponse, cest non. Et vous commencez donc comprendre tout lintrt de LINQ mme avec de simples liste dobjets (et je ne parle pas des graphes dobjets !) Laffichage est ralis par ObjectDumper qui est une petite classe outil ralisant tout simplement un foreach pour lister la console toute collection qui lui est passe. La classe utilise la rflexion pour afficher le nom des proprits avec leur valeur. Elle na rien voir avec LINQ, cest une astuce pour afficher rapidement des donnes. Le code source, provenant de Microsoft, est fourni avec le code des exemples de larticle.

Construction de la requte
En dehors de lobjet utilitaire daffichage que vous ne connaissez pas et qui na pas dintrt dans nos explications, le reste est dune grande lisibilit : nous obtenons le chemin des fichiers temporaires de Windows puis nous construisons une requte LINQ. LINQ ressemble beaucoup ici SQL mais avec certaines spcificits. La premire chose et la plus importante cest quil sagit de code C#, pas dune chane de caractres ne voulant rien dire pour le langage. Il est ainsi contrl la compilation et on dispose dIntellisense lors de son criture. Bien entendu, vous noterez aussi que le plus puissant des SQL ne

Page 5

pourrait rien dans ce cas prcis puisque la source est une liste renvoye par lOS via le Framework .NET. Ensuite vous remarquez que par rapport SQL la requte est en quelque sorte inverse . La clause from est place en premier et la clause select en dernier Pourquoi ce choix qui peut sembler curieux de prime abord ? Lexplication ma t donne par Luca Bolognese dans sa confrence sur LINQ aux TechEd 2007 Barcelone (confrence TLA 308 pour ceux qui y ont particip et qui souhaiterait la voir ou la revoir sur le DVD) : Visual Studio possde une fonction essentielle pour acclrer le dveloppement, Intellisence. Et pour que ce dernier puisse fonctionner correctement il faut quil sache sur quoi on travaille, ce qui est le cas le plus souvent. Mais dans une requte SQL (ou de type SQL) si on commence par taper le select Intellisense sera dans limpossibilit de fournir une aide concernant les champs disponibles puisque le dveloppeur na pas encore tap la clause from Donc pour permettre Intellisense dtre utilisable mme lors de la frappe des requtes LINQ il fallait que le nom de la ou des sources de donnes, le from, se trouve en premier. Cest tout bte. De plus, Luca parle aussi dexpriences menes par Microsoft o des dveloppeurs purement SQL taient placs devant des crans pour taper des requtes, le tout sous lil indiscret dobservateurs professionnels cachs derrire des vitres sans tain. Lors de ces expriences il a pu tre noter que le plus souvent un dveloppeur SQL commence par taper la clause from pour savoir sur quoi porte sa requte puis quil remonte pour taper le select. LINQ ne ferait donc que reprendre lordre rel dans lequel les dveloppeurs saisissent du SQL, mme sans en avoir conscience Bref, LINQ commence par la clause from dans laquelle on nomme une source (et aussi dventuelles jointures comme nous le verrons ultrieurement), dans notre exemple la source est fournie par la collection retourne par GetFiles() de lobjet DirectoryInfo. Dans cette requte on dcide dappeler chaque lment de cette source f, au mme titre que dans un foreach on nomme la variable qui sera renseigne chaque passage dans la boucle. Il ne faut pas confondre cette notation qui ressemble au premier coup dil un alias de table en SQL (FROM Customer CLIENTS). Sous SQL il sagit bien de lalias de lensemble de donnes (la table), alors quici il sagit dun lment de la collection. A ce stade notre requte possde donc une source de donnes (la liste des fichiers du rpertoire temporaire de lOS), source qui alimentera chaque passage une variable appele f. Le type de f est dduit de celui de la collection automatiquement (infrence de type comme par le mot cl var, voir mon prcdent article sur les nouveauts de C# 3.0). On retrouve ensuite des mots cls classiques de SQL comme la clause where. Ici elle nous sert filtrer sur la taille des fichiers retourns. La clause orderby fonctionne comme son homonyme SQL, elle trie les donnes retournes, ici en ordre descendant avec lajout de descending. La clause select de lexemple est peut-tre celle qui sera la moins simple comprendre de prime abord mme si son nom est familier en SQL.

Page 6

En ralit, dans la version la plus simple nous aurions pu crire select f ; c'est--dire retourne llment f de la collection qui rpond aux critres de la requte. Dans ce cas la variable query, qui contient (virtuellement) le rsultat de la requte aurait t une collection dobjets FileInfo. Mais nous ne sommes pas intresss ici par toutes les informations de FileInfo, seuls le nom du fichier, sa taille et sa date de dernire criture sont utiles pour cette application (choix arbitraire pour la dmo, bien sr). Pourquoi se charger dobjets plus lourds si notre besoin est plus lger ? LINQ permet de rpondre cette question trs facilement : par le mot cl new qui va permettre de retourner une instance de tout type et donc, aussi, un type anonyme (voir mon article sur les nouveauts de C# 3.0), ce type anonyme, cette nouvelle classe sans nom, sera constitue de trois champs, les trois que nous listons. Nous ne leur donnons pas de nom car LINQ est assez fin pour les dduire de ceux des proprits de f (une instance de FileInfo) Nindiquant aucun nom, LINQ reprend automatiquement ceux des proprits de lobjet utilis (ceux de la classe FileInfo donc). Voil ce quest LINQ to Objects et voil par un exemple on ne peut plus simple pourquoi cette nouveaut de C# 3.0 va devenir aussi indispensable que tout le reste du langage : tout est donnes, mme une liste de fichiers retourne par lOS et cette liste peut tre filtre, trie en quelques mots en bnficiant de Intellisense Un dernier mot. Un peu plus haut je parle de la variable query (dclare par var) et jajoutais quelle contient le rsultat de la requte mais virtuellement . Pourquoi cette nuance ? Simplement parce les requtes LINQ ne sont excutes quau moment o leur rsultat est numr. Dans notre code cela lieu dans la boucle de lobjet ObjectDumper. Le fait dcrire une requte LINQ ne dclenche rien. On peut ainsi les regrouper un mme endroit pour plus de lisibilit ou dautres raisons, elles ne dclencheront leur traitement quau moment o on tentera dnumrer les lments retourns.

LINQ to ADO.NET
Nous venons de voir que LINQ to Objects est une fonctionnalit de base de C# 3.0 quon peut utiliser sur toute collection dobjets (ou graphe dobjets). Nous navons utilis quune liste simple dans lexemple prcdent, mais LINQ fonctionne sur des graphes (avec des jointures), c'est--dire sur des proprits qui retournent dautres collections. Par exemple la fiche dun client possdera une proprit commandes qui est une collection de toutes les commandes de ce client qui elles-mmes ont une proprit lignes retournant les lignes de chaque commande, etc. LINQ autorisera la navigation dans telles proprits et sous-proprits, comme on navigue sur les relations dune base de donnes. Lexemple de LINQ to Objects ne montrait pas cette possibilit car nous allons avoir dautres occasions de mettre en uvre la gestion de vrais graphes dobjets, notamment avec LINQ to ADO.NET. Gardez seulement lesprit que cela sapplique aussi LINQ to Objects.

Page 7

LINQ to ADO.NET, cest quoi ?


On appelle LINQ to ADO.NET lensemble des implmentations de LINQ qui, dune faon ou dune autre, doivent accder des donnes relationnelles accessible via les fournisseurs daccs de ADO.NET. LINQ to ADO.NET regroupe ainsi trois saveurs diffrentes de LINQ : LINQ to SQL Cette version de LINQ gre le mapping entre des types C# et les enregistrements dune base de donnes. Cest en quelque sorte le niveau dentre de LINQ to ADO.NET. LINQ to Dataset Il sagit de la possibilit dinterroger avec LINQ les donnes contenues dans un Dataset. LINQ to Entities Cette version de LINQ possde un niveau dabstraction suprieur LINQ to SQL. Au lieu dinterroger la base de donnes et davoir une sorte dquivalence entre classes manipules par LINQ et tables ou vues prsentes dans la base de donnes, LINQ to Entities permet dcrire des requtes qui portent sur un modle conceptuel, modle lui-mme mapp sur la base physique. Nous en verrons aussi un exemple car il sagit de la version de LINQ la plus sophistique et la plus puissante.

LINQ to XML
Cette version de LINQ a t adapte pour permettre la manipulation de donnes XML. On retrouve ici toute la justification et toute la puissance de LINQ au service des donnes XML, de plus en plus prsentes mais toujours aussi lourdes exploiter. Grce LINQ to XML, et par lajout de quelques spcificits permettant de grer les balises XML, il devient possible dcrire des requtes complexes sur des sources XML en quelques instructions et mme de construire des fichiers XML de faon intuitive. Loin dtre anecdotique, LINQ to XML rvolutionne lui aussi par ses ajouts syntaxiques la faon de travailler avec des donnes XML.

Les exemples
Le but de cet article est de vous fournir une vision densemble de LINQ et pour cela, plutt que de rester dans la thorie je souhaite vous montrer du code au travers dexemples concrets et parlants. En revanche je ne traiterai pas en dtail de la syntaxe utilise, la documentation de Microsoft me semble suffisamment claire pour ne pas en faire une redite (et je suis certain que vous comprendrez aussi quun article ne peut se transformer en un livre !).

LINQ to SQL
Si LINQ to Objects est dj en soi un grand progrs, avec LINQ to SQL on franchit encore une tape qui marquera un tournant dans les langages de programmation, jen suis convaincu. En effet, depuis des dcennies les dveloppeurs passent une bonne partie de leur temps tenter de marier deux mondes totalement disjoints et sloignant avec le temps : dune

Page 8

part les langages, dabord procduraux, puis simplement orients objet jusquau langages comme C# totalement objet, et, dautre part, les bases de donnes, dabord simples fichiers indexs, puis base de donnes relationnelles de plus en plus sophistiques. Deux mondes qui ont connu un essor aussi important mais dans deux directions qui ne se sont jamais rejointes. Les bases de donnes, comme je le soulignais en dbut darticle, ne savent travailler que sur un modle ligne / colonne, avec une entit de base, la table (appele aussi relation). Cest mme leur fondement, la toute premire des 12 rgles du docteur Codd qui fonda le modle relationnel dans son papier A Relational Model of Data for Large Shared Data Banks en 1970 (la rgle n1 impose que toute linformation soit prsente uniquement sous la forme de lignes et de colonnes une table). Quelles que soient les volutions des SGBD, lajout des champs Blobs, le support des donnes texte longues, des donnes XML, voire des objets , tout cela reste rgi par les 12 rgles de Codd et ne peut sen carter au risque de faire crouler ldifice technologique et industriel des SGDB relationnels. Do la naissance des SGBD objet. Mais si le monde des SGBD relationnels est bien balis, reposant sur des technologies prouves, il nen va pas de mme des SGBD-O. Le premier frein est leur lenteur lgendaire, le second est que par manque de standard impos par les faits, chacune possde un langage dinterrogation plus ou moins sotrique (API complexes ou variantes exotiques de OQL gnralement). Pire, les objets de ces SGBD-O nont que trs peu de chance de correspondre aux objets des langages utiliss par les dveloppeurs ce qui rclament nouveau un systme de traduction et de mapping On ne gagne donc pas grand-chose et on se complique surtout la vie (et la maintenance !). On constate ainsi que malgr quelques tentatives intressantes les SGBD-O ne rencontrent le plus souvent quun succs destime comme en ce moment DB4O ou mme O2. Bref, les SGBD-R ont encore de trs beaux jours devant eux car ils sont standardiss, rapides, puissants et de nombreux acteurs de ce march sont implants depuis suffisamment longtemps pour que chacun puisse faire un choix clair et bnficier de produits rapides, puissants et fiables. SQL Serveur, MySQL, Sybase, DB2 ou Oracle sont des exemples trs factuels de ces avantages. Ce constat serait-il celui dun statu quo, dun mariage dfinitivement impossible entre langage de programmation Objet et SGBD-R ? Comment imaginer marier ces bases de donnes relationnelles coinces dans leur logique en deux dimensions, ne sachant travailler que sur des rectangles, ne sachant retourner que des rectangles, avec des langages objets eux aussi de plus en plus sophistiqus et travaillant sur des graphes ? Cest le O/R mapping (ou ORM, object-relational mapping), c'est--dire un middleware qui on apprend la fois comment les donnes sont stockes dans la base de donnes et comment elles sont reprsentes en mmoire par des objets du langage de programmation. A sa charge de crer ces derniers partir des informations contenu dans la base. LORM est une technique intressante plus dun titre puisquelle permet au dveloppeur de manipuler des objets en conformit avec la puissance des langages modernes tout en exploitant, pour la persistance, des SGBD-R fiables et largement rpandus dans les entreprises.

Page 9

Le seul problme de lORM cest quil nexiste l non plus aucun vrai standard industriel ni mme technique, puisque cela va dune simple couche de type Data Access Layer (DAL) des Frameworks entiers disposant de leur propre langage dinterrogation, bref, on trouve de tout. Faire un choix est chose ardue. Et quand un dveloppeur ne sait pas comment choisir, il ne choisit pas : il vite et contourne le problme Le Framework .NET nous permettait dj dcrire des couches daccs aux donnes objectives, les DAL voqus ci-dessus. Il sagit dailleurs plus dune sorte de design pattern que dune innovation de la plateforme, cest une faon disoler le code de la source de donnes trs efficace et lgante utilisable, et utilise, sous dautres langages. Pour aller encore plus loin le Framework .NET a propos les Datasets typs, le mapping est automatis, mais lobjet final, le Dataset nest quune objectivation grossire de la source, il continue de prsenter des donnes rectangulaires, pas des graphes dobjets, faisant remonter telle une bulle les concepts et la rigidit des SGBD au niveau des objets et du langage. On oblige toujours le dveloppeur et son langage Objet se plier la logique des rectangles, le progrs est purement stylistique et pratique : moins de code saisir (gnration automatique de Visual Studio), collections de lignes de donnes plus simples manipuler que des requtes SQL individuelles, typage des informations. Il fallait donc aller plus loin. Il fallait rsoudre le mapping sa plus simple expression, viter les redites dans le code, unifier les concepts et surtout les rendre accessibles naturellement, en faire une extension du langage et de la plateforme. LINQ to ADO.NET cest cela. LINQ to SQL nest que le premier niveau, celui que nous allons voir maintenant. LINQ to Entities va encore plus loin pour atteindre le niveau dabstraction le plus lev possible. Commenons par le dbut donc, voici LINQ to SQL. Dans sa version la plus simple, LINQ to SQL permet de mapper nimporte quelle classe nimporte quelle table dune base de donnes. Nous utiliserons la clbre base Northwind qui, si vous ne la connaissez pas encore, est une base de donnes classique de type clients / commandes / articles fournie avec SQL Server et utilise systmatiquement dans les dmos, rituel auquel je vais faire allgeance Les bases du mapping Je vous propose dans un premier temps de crer une classe Client toute simple :
public class Client { public string CustomerID { get; set; } public string ContactName { get; set; } public string Country { get; set; } }

Difficile de faire plus simple La classe expose trois proprits, lidentificateur du client, le nom du contact et le pays du client. Nota : les proprits sont ici dclares en utilisant une nouveaut de C# 3.0. Cette notation ressemble comme deux gouttes deau celle dune interface. Les accesseurs (get et set) sont indiqus mais sans code. La ressemblance est

Page 10

frappante mais elle sarrte l. Nous dfinissons bien une proprit normale , sauf que C# crera lui-mme le champ priv correspondant (on peut voir les noms de ces champs dans le code IL aprs compilation). Cest une faon rapide et pratique de crer des proprits sans, au dpart, implmenter de champ priv ou daccesseurs complexes, tout en laissant la possibilit de faire voluer le code ultrieurement. Comment mapper cette classe sur la table Customers de Northwind ? Il suffit dutiliser des attributs disponibles par System.Data.Linq. Le premier attribut sapplique toute la classe et permet dindiquer quelle table elle correspond. Le second attribut sapplique chaque proprit qui possde un pendant dans les colonnes de cette table. Le code de notre classe devient alors :
[Table(Name="Customers")] public class Client { [Column(IsPrimaryKey = true)] public string CustomerID { get; set; } [Column] public string ContactName { get; set; } [Column] public string Country { get; set; } }

On peut voir ci-dessus lattribut Table ainsi que lattribut Column. Ces attributs supportent des proprits ou des constructeurs personnaliss pour affiner leur signification. Ainsi lattribut Table possde une proprit Name qui permet dindiquer le nom physique de la table dans la base de donnes puisque notre classe possde un nom trs diffrent. De mme lattribut Column supporte, entre autre, la proprit IsPrimaryKey pour indiquer que la proprit est la cl primaire de la table, permettant LINQ de grer cette contrainte. Comme dans cet exemple jai utilis des noms de proprits identiques aux noms des colonnes de la table il ny a rien ajouter. On pourrait bien entendu faire comme pour la table et prciser le nom des champs dans lattribut Column. Tout cela reste donc simple tout en offrant une certaine souplesse. Un contexte dexcution Pour exploiter notre classe Client, nous devons disposer dun contexte faisant le lien entre cette dernire et la base de donnes, pour ce faire nous allons driver notre propre classe de System.Data.Linq.DataContext en y ajoutant une proprit pour la table des clients. Cela seffectue en deux lignes de code :

Page 11

public class DemoContext : DataContext { public Table<Client> Customers; public DemoContext(string fileOrServerOrConnection) :base(fileOrServerOrConnection) }

{}

En crant notre propre classe drive de DataContext nous pourrons trs facilement accder la table des clients qui en devient une proprit (Customers). Nous verrons quil existe une autre faon de faire qui vite mme davoir driver DataContext. Une fois cela crit, cest fort peu vous en conviendrez, nous pouvons travailler sur des objets Client provenant de la base de donnes sans aucun contact avec le monde des SGBD ! Premire requte
public static void LanceDemo() { const string northWind = "chane de connexion ado.net"; DemoContext db = new DemoContext(northWind); db.Log = Console.Out; // affiche le SQL sur la console var query = db.Customers; // requte ultra simple: toute la table ObjectDumper.Write(query); // affiche le rsultat Console.ReadLine(); // pause clavier }

Voici un extrait de la sortie console que produit se code :


SELECT [t0].[CustomerID], [t0].[ContactName], [t0].[Country] FROM [Customers] AS [t0] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8 CustomerID=ALFKI CustomerID=ANATR CustomerID=ANTON CustomerID=AROUT CustomerID=BERGS CustomerID=BLAUS CustomerID=BLONP ContactName=Maria Anders Country=Germany ContactName=Ana Trujillo Country=Mexico ContactName=Antonio Moreno Country=Mexico ContactName=Thomas Hardy Country=UK ContactName=Christina Berglund Country=Sweden ContactName=Hanna Moos Country=Germany ContactName=Frdrique Citeaux Country=France

Dans la capture ci-dessus on remarque le texte de la requte SQL rellement envoye base de donnes, cest une astuce pratique de dbogage sur laquelle je vais revenir plus bas. Restons concentrs sur la liste qui suit : on obtient bien la liste de tous les clients de la table Customers de la base de donnes Northwind, chaque fiche possdant bien les trois seules proprits de la classe Client que nous avons vu plus haut. Et cest tout ! Peut-on rver ORM plus simple et intgr plus naturellement au langage ? Certes, pour linstant nous restons accrochs au modle physique avec un mapping posant une quivalence entre une classe C# et une table de la base de donnes. Cest LINQ to SQL. Pour atteindre le niveau suprieur nous devons utiliser LINQ to Entities. Mais pas de prcipitation ! Revenons sur le code de lexemple.

Page 12

Tout dabord vous remarquerez la constante qui dfinit la chane de connexion ADO.NET vers la base de donnes. Cette chane peut aussi tre issue du fichier de paramtrage de lapplication ou de lutilisateur, bien entendu. Ensuite nous crons une instance de notre DataContext personnalis, DemoContext, auquel nous passons la chane de connexion, la variable rsultante est db. Petite astuce au passage, comme nous voulons voir pour les besoins de la dmo le code SQL qui est rellement envoy la base de donnes, nous faisons pointer la proprit Log du DataContext vers le flux de sortie de la console. Simple et pratique. Nous verrons plus loin quel point les requtes LINQ, tout en restant simples, peuvent gnrer un SQL bien form mais complexe que nous navons pas, heureusement et grce LINQ, crire ! La requte que nous utilisons dans cet exemple est la plus simple quon puisse concevoir : nous demandons la totalit de la table des clients. Plus de lignes et de colonnes, plus de tables, plus de donnes rectangulaires, rien que des instances de la classe Client, cest LINQ qui soccupe de discuter avec le SGBD dont nous navons plus rien savoir. Plus de couche D.A.L. (Data Access Layer) non plus, ni mme ventuellement de B.O.L. (Business Object Layer) puisque la classe Client remplit le rle de D.A.L. et quen y ajoutant un peu de code elle pourrait devenir dans des cas simples en mme temps un B.O.L. ! Variante Lcriture de la classe DemoContext drive de DataContext nest certes pas bien complique deux lignes de code trs utiles puisque notre classe sait ensuite fournir la liste des tables quelles connat (nous aurions pu ajouter de la mme faon que les clients, les articles, les commandes etc.). Mais il est possible de se passer de cette criture en utilisant les gnriques. Dans ce cas, le seul code concevoir est celui de la classe Client A lexcution notre code devient le suivant :
public static void LanceDemo2() { const string northWind = "chane de connexion"; DataContext db = new DataContext(northWind); Table<Client> Clients = db.GetTable<Client>(); db.Log = Console.Out; var query = Clients; ObjectDumper.Write(query); Console.ReadLine(); }

Dans cette version nous crons directement une instance de DataContext sans avoir driv cette classe. Bien entendu, DataContext ne sait rien de notre table des clients, il nest donc plus possible de lutiliser pour obtenir cette dernire. Nous crons ainsi la vole une variable Clients de type Table<Client> dont le contenu est rcupr par GetTable<Client>();

Page 13

La requte est ensuite la mme. En ralit elle devient superflue La variable Clients contient dj (potentiellement) tous les clients. Nous pouvons supprimer la variable query et passer directement la variable Clients lobjet afficheur :
public static void LanceDemo2() { const string northWind = "Data Source=\\sqlexpress;Initial Catalog=Northwind;Integrated Security=True"; DataContext db = new DataContext(northWind); db.Log = Console.Out; Table<Client> Clients = db.GetTable<Client>(); ObjectDumper.Write(Clients); Console.ReadLine(); }

Vous pourrez dailleurs vrifier sur la console que le DataContext gnre toujours la mme requte SQL. Dans cette version simplifie nous navons fait que dcrire la classe Client avec deux attributs (Table et Column). Rien dautre. Et il nous est possible dobtenir des clients, de les filtrer, de les trier mais aussi de les modifier. Nous verrons cela un peu plus loin.

Compliquons un peu la requte


Dans lexemple qui prcde nous avons tellement peu utilis le requtage de LINQ to SQL que la dernire version du code pouvait mme se passer dune variable pour la requte. Il tait intressant de voir luvre les mcanismes de base de LINQ, mais le temps est venu dcrire de vraies requtes comme nous le ferions en SQL, mais sur des objets avec LINQ. Nous reprendrons la mme structure de code et le mme objet DemoContext driv de DataContext exposant la proprit Customers. Filtrage La premire chose quon demande une requte est bien souvent de filtrer les donnes. Essayons dobtenir uniquement les clients situs Londres :
public static void LanceDemo3() { DemoContext db = new DemoContext(northWind); db.Log = Console.Out; var query = from c in db.Customers where c.Ville == "London" select c; ObjectDumper.Write(query); Console.ReadLine(); }

Le principe restant le mme nous nous concentrerons ici uniquement sur la requte. Elle reste trs simple et comprhensible pour qui connat dj SQL, et mme pour les autres dailleurs, pour peu quon dispose dun minimum de vocabulaire anglais. Si nous regardons la trace SQL (db.Log) la requte envoye la base de donnes devient dsormais :

Page 14

SELECT [t0].[CustomerID], [t0].[ContactName], [t0].[Country], [t0].[City] AS [Ville] FROM [Customers] AS [t0] WHERE [t0].[City] = @p0 -- @p0: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [London] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

On remarque que LINQ to SQL utilise systmatiquement des paramtres, ici @p0. On peut aussi voir la liste des paramtres et de leurs valeurs sous la requte. La trace Log du DataContext est trs instructive pour qui veut vrifier le SQL transmis la base de donnes. En effet, outre lintrt pdagogique que nous exploitons ici, il peut savrer dans certains cas ncessaire de proposer sa propre requte, voire le nom dune procdure stocke pour atteindre les donnes. LINQ to SQL le gre aussi. Et pour dcider quel code sera le plus efficace il savre indispensable dobtenir le code SQL produit par LINQ pour linspecter, le Log rpond ce besoin. Tri Trier des donnes est tout aussi frquent que de les filtrer. Voici un exemple de la mme requte avec un tri :
public static void LanceDemo4() { DemoContext db = new DemoContext(northWind); db.Log = Console.Out; var query = from c in db.Customers where c.Ville == "London" orderby c.ContactName ascending select new { c.ContactName, c.Ville }; ObjectDumper.Write(query); Console.ReadLine(); }

Ici nous trions les clients retourns par ordre ascendant du nom du contact. Au passage nous ne retournons pas des instances de Client mais un type anonyme ne contenant que deux proprits, le nom du contact et le nom de la ville. La trace complte la console donne ceci :

Page 15

SELECT [t0].[ContactName], [t0].[City] AS [Ville] FROM [Customers] AS [t0] WHERE [t0].[City] = @p0 ORDER BY [t0].[ContactName] -- @p0: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [London] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8 ContactName=Ann Devon Ville=London ContactName=Elizabeth Brown Ville=London ContactName=Hari Kumar Ville=London ContactName=Simon Crowther Ville=London ContactName=Thomas Hardy Ville=London ContactName=Victoria Ashworth Ville=London

Autre syntaxe Arriv ce stade de larticle je pense que vous commencez comprendre comment marche LINQ. Mais en ralit ce que nous avons vu nest quune toute petite partie de la syntaxe de LINQ et donc de ses possibilits Rappelons que ce que vous tes en train de lire est juste un article prsentant LINQ et non un cours sur LINQ. Je vous encourage vivement consulter la documentation de Microsoft. Et comme je suis taquin et que je ne voudrais surtout pas vous laisser croire quil ny a pas grand-chose dautre voir, je vais vous prsenter une autre requte LINQ to SQL que je nexpliquerais pas, juste pour crer un peu de frustration et vous donnez lenvie daller chercher au-del de cet article
public static void LanceDemo5() { DemoContext db = new DemoContext(northWind); // db.Log = Console.Out; var query = db.Customers.GroupBy(c => c.Ville, c => c.Ville+": "+c.ContactName). Skip(10).Take(8); ObjectDumper.Write(query,1); Console.ReadLine(); }

Quelques indices malgr tout : la requte prend les 8 premiers clients qui suivent les 10 premiers de la table, une fois celle-ci groupe sur la ville de chaque client. Vous navez pas tout compris ? Je vous lai dit, cest fait exprs ! La trace de sortie est :
... Brcke: Maria Larsson ... Brandenburg: Philip Cramer ... Bruxelles: Catherine Dewey ... Buenos Aires: Patricio Simpson Buenos Aires: Yvonne Moncada Buenos Aires: Sergio Gutirrez ... Butte: Liu Wong ... Campinas: Andr Fonseca ...

Page 16

Caracas: Manuel Pereira ... Charleroi: Pascale Cartrain

Vous retrouverez bien entendu le code de tous les exemples de cet article dans les projets VS 2008 fournis dans le mme fichier Zip que le PDF que vous lisez en ce moment. Concluons sur LINQ to SQL Lintention de cet article nest pas, comme je lai dj mentionn, dplucher la syntaxe de LINQ, mais de faire un tour de la technologie avec des exemples pratiques et simples. Nous nallons ainsi pas continuer taper du mapping la main alors quil existe une faon bien plus simple et lgante dutiliser LINQ avec une base de donnes, LINQ to Entities. Cest lobjet dune section venir, aprs quelques exemples de LINQ to Dataset et de LINQ to XML.

LINQ to Dataset
LINQ to Dataset est une extension de LINQ permettant de travailler directement sur le contenu de Datasets typs ou non. Larticle est fourni avec un projet exemple qui montre quelques manipulations dun Dataset typ, nhsitez pas jouer avec. LINQ to Dataset est tout aussi puissant et versatile que les autres versions de LINQ. Je mettrais juste un bmol lutilisation de LINQ sur les Datasets : la bonne pratique (et la RAM de votre machine !) interdit de charger toute une base de donnes dans un Dataset Ds lors ce dernier ne doit contenir quun sous-ensemble minimal des donnes de la base, ce qui implique de lavoir charg en effectuant dj un premier filtrage. LINQ to Dataset se place donc aprs cette premire slection purement SQL pour ne travailler que sur les donnes en RAM.Cela, mon sens, restreint bien entendu lutilit de LINQ ici puisquun bon Dataset est un Dataset presque vide Mais quimporte, puisquon peut se servir de LINQ aussi sur les Datasets, je fais confiance votre imagination pour en tirer avantage ! Exemple Reprenons la base de donnes Northwind et les tables Customers et Orders dont nous avons tir un Dataset fortement typ par les mcanismes usuels de Visual Studio. La classe sappelle NorthwindDataset et son schma est dcrit dans le fichier xsd accompagn du code source C# produit par VS. Je passerais sur la faon de crer une instance de ce Dataset et de remplir les deux tables avec les donnes issues de la base, puisquil sagit ici de procds dj connus depuis longtemps de ADO.NET. Nous en arrivons ainsi la requte LINQ to Dataset. Celle-ci va utiliser une jointure dclarative entre les clients et les commandes afin de slectionner tous les clients (ainsi que leurs commandes) dont le nom du contact commence par la lettre A.

Page 17

var query = from c in ds.Customers join o in ds.Orders on c.CustomerID equals o.CustomerID into orderlines where c.ContactName.StartsWith("A") select new { CustID = c.CustomerID, CustName = c.ContactName, Orders = orderlines };

Les entits retournes sont formes de lidentificateur du client, de son nom et de la liste de ses commandes. Ce type est anonyme, cr la vole. On remarquera que nous avons choisi de donner des noms de proprits diffrents des noms dorigines, par exemple lidentificateur sera appel CustID et non plus CustomerID. De la mme faon vous pouvez voir comment lensemble des commandes dun client est stock (virtuellement) dans une variable interne la requte (orderlines) et comment cette grappe dobjets est passe dans lobjet rsultat (proprit Orders). Tout cela na valeur que dexemple de syntaxe et na aucun caractre obligatoire ou fonctionnel bien entendu. La jointure entre les clients et les commandes est effectue la main en utilisant join dune faon similaire la syntaxe SQL de mme fonction. Le rsultat de cette jointure est nomm (orderlines) pour tre facilement r-exploit dans la requte. On notera que la jointure ne fonctionne que sur lgalit et que pour marquer cette obligation LINQ force lutilisation du mot equals au lieu dune galit de type ==. Il sagit ici dviter toute confusion. Si == tait autoris on pourrait tre tent dutiliser un autre oprateur de comparaison or cela naurait pas de sens pour LINQ cet endroit. En utilisant un mot cl particulier, LINQ marque ainsi une utilisation particulire et spcifique de lgalit. Il ne sagit pas dune interprtation personnelle mais dune explication donne par un membre de lquipe LINQ aux TechEd Microsoft Barcelone en octobre dernier, cest donc une vraie info, certes anecdotique, mais de premire qualit. LINQ to Dataset est une adaptation de LINQ travaillant en mmoire, de fait il ny a pas de code SQL gnr visualiser. Je vous fais grce de la sortie console qui liste tous les objets, cela ne vous dirait rien. Le projet exemple est de toute faon fourni avec larticle pour que vous puissiez exprimenter la chose par vous-mmes. Regardons maintenant comment, au lieu de crer la jointure en LINQ, nous pourrions exploiter la relation existante dans le Dataset typ :
var query2 = from c in ds.Customers where c.ContactName.StartsWith("A") && c.GetOrdersRows().Any(sv=> sv.ShipVia==2 && sv.ShippedDate.Year>1997) orderby c.ContactName ascending select new { c.ContactName, Orders = c.GetOrdersRows() };

Ici la requte est plus complexe. Nous souhaitons obtenir une liste contenant des lments (type anonyme) constitus dun nom de contact client et des commandes de ce client. Cela est rgl par la partie select de linstruction.

Page 18

Toutefois nous souhaitons que seuls les clients dont le nom de contact commence par la lettre A soient slectionns. Cest la premire partie du where qui sen charge. Mais pour compliquer les choses nous dsirons que seuls les clients ayant au moins une commande passe aprs lanne 1997 et livre via le mode 2 (peu importe ce que signifie ce code) soient slectionns. Cela est pris en charge par la seconde partie du where en utilisant la relation GetOrdersRows qui retourne les commandes du client en cours danalyse par LINQ. Cette mthode a t gnre automatiquement par Visual Studio dans le Dataset typ. On remarque lutilisation du class helper Any provenant de LINQ et de ses paramtre sous la forme dune expression Lambda. Comme on le voit, LINQ to Dataset est tout aussi sophistiqu que les autres manations de LINQ. En ralit dailleurs nous navons rien utilis qui soit ici spcifique LINQ to Dataset, les requtes proposes en exemple auraient pu tre effectue avec LINQ to SQL notamment. Seule la source de donnes est diffrente (mapping dune classe vers une table avec LINQ to SQL ou utilisation des tables dun Dataset avec LINQ to Dataset). Et cest bien l la force de LINQ, il en existe des versions spcialises selon le type de la source de donnes, mais sa syntaxe et sa puissance ne changent pas.

LINQ to XML
Cette version de LINQ repose sur les mmes bases que tout ce que nous avons vu jusqu maintenant mais elle introduit quelques lments syntaxique propres. Sa spcificit est de pouvoir travailler sur des sources XML, voire de crer des fichiers XML. Plus de Dataset ni de DataContext ici, juste des documents XML, sur disque ou en mmoire. Il est donc possible avec LINQ to XML dinterroger des documents XML de la mme faon quun Dataset ou quune liste dobjet. Les exemples de syntaxe vus jusquici sont applicables des sources XML. Toutefois XML utilise un formalisme bien particulier, il existe donc toute une syntaxe LINQ to XML bien spcifique ce contexte. Ce que nous allons voir maintenant. Lun des gros avantages de LINQ to XML est de permettre l linterrogation et la cration de documents XML en se passant totalement dAPI plus ou moins complexes, et mme de XPath, XQuery ou XSLT ! LINQ to XML na pas vocation remplacer ces technologies, il se place juste au dessus pour donner au dveloppeur un niveau dabstraction suprieur et simplifier la manipulation des sources XML. LINQ to XML propose un modle particulier pour accder aux donnes XML, les sources pouvant tre un flux (stream), un fichier ou du XML en mmoire. En ralit il y trs peu de choses savoir pour utiliser le modle LINQ to XML, il suffit de connatre les types les plus courants de ce dernier qui sont : XDocument, XElement et XAttribute. Chacun possdant des constructeurs permettant de contrler les objets crs avec prcision. On comprend, bien entendu, rien que par leur nom la vocation de ces classes et leur relation hirarchique calquant celle du formalisme XML (document / lment / attribut). Crer des donnes XML Regardons dabord comment il est facile laide des classes indiques ci-dessus de crer un fichier XML par code :

Page 19

public static void Demo1() { XElement xml = new XElement("clients", new XElement("client", new XAttribute("ID", 2), new XElement("socit", "e-naxos"), new XElement("site", "www.e-naxos.com")), new XElement("client", new XAttribute("ID", 5), new XElement("socit", "Microsoft"), new XElement("site", "www.Microsoft.com"))); Console.WriteLine(xml); Console.ReadLine(); }

La sortie console de cet exemple sera le contenu de la variable xml :


<clients> <client ID="2"> <socit>e-naxos</socit> <site>www.e-naxos.com</site> </client> <client ID="5"> <socit>Microsoft</socit> <site>www.Microsoft.com</site> </client> </clients>

En utilisant XElement nous pouvons totalement crer le corps dun fichier XML, mais pour satisfaire la norme XML ce corps peut tre inclus dans un document (ce qui nest pas indispensable pour le requtage LINQ). Pour cela on utilise la classe XDocument. Lexemple suivant cr un fichier bien form en exploitant cette possibilit, fichier qui est sauvegard sur disque dans la foule.
public static void Demo2() { XDocument doc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XComment("Liste des clients"), new XElement("clients", new XElement("client", new XAttribute("ID", 2), new XElement("socit", "e-naxos"), new XElement("site", "www.e-naxos.com")), new XElement("client", new XAttribute("ID", 5), new XElement("socit", "Microsoft"), new XElement("site", "www.Microsoft.com")))); doc.Save("ClientsDemo.xml"); Console.WriteLine(doc); Console.ReadLine(); }

Sur disque nous trouvons alors un fichier ClientsDemo.xml dont le contenu visionn par le bloc-notes est le suivant :

Page 20

<?xml version="1.0" encoding="utf-8" standalone="yes"?> <!--Liste des clients--> <clients> <client ID="2"> <socit>e-naxos</socit> <site>www.e-naxos.com</site> </client> <client ID="5"> <socit>Microsoft</socit> <site>www.Microsoft.com</site> </client> </clients>

LINQ to XML ne se limite ainsi pas uniquement un requtage simplifi des donnes XML, il permet aussi den crer trs facilement. Marier LINQ to XML avec LINQ to SQL/Dataset/Entities Produire des donnes XML est aujourdhui une activit courante pour une grande partie des applications. Produire ces donnes partir dautres donnes, notamment celles contenues dans une base SQL ou dune liste dobjets en mmoire apparat ainsi dune grande utilit. Ds lors pourquoi ne pas marier LINQ to XML et sa possibilit de crer simplement des fichiers XML avec LINQ to Dataset, LINQ to SQL ou LINQ to Entities ? Pourquoi pas en effet Rien ne sy oppose. Voyons dans un exemple comment raliser en quelques lignes ce qui prendrait des pages de code avec dautres mthodes. Explications pralables : nous allons utiliser comme source la classe Client et le DataContext spcialis de lexemple de LINQ to SQL tudi plus haut dans cet article. Le code de ces dclarations ne sera pas repris ici pour allger le texte.
public static void Demo3() { const string northWind = "chane de connexion"; using (DemoContext db = new DemoContext(northWind)) { XElement xml = new XElement("clients", from c in db.Customers where c.ContactName.StartsWith("A") orderby c.ContactName select new XElement("client", new XAttribute("ID", c.CustomerID), new XAttribute("contact", c.ContactName)) ); Console.WriteLine(xml); xml.Save("ContactsDemo.xml"); } Console.ReadLine(); }

Ce code cr un corps de document XML partir des instances de la classe Client dont le nom de contact commence par la lettre A, la liste tant trie par ordre alphabtique de ces derniers. Le select cr les nouvelles entres, la table elle-mme est cre par le XElement de tte sappelant xml. Pour crer un document bien form et lenregistrer sur disque par exemple, il suffit denchsser le rsultat xml dans un XDocument et de le

Page 21

sauvegarder par la mthode vu prcdemment. Lutilisation dun XDocument nest en rien indispensable et pour le prouver nous nous en passerons dans cet exemple. La sortie console de lexemple est la suivante :
<clients> <client ID="ROMEY" <client ID="MORGK" <client ID="ANATR" <client ID="TRADH" <client ID="GOURL" <client ID="EASTC" <client ID="LAMAI" <client ID="ANTON" <client ID="FAMIA" <client ID="SPLIR" </clients>

contact="Alejandra Camino" /> contact="Alexander Feuer" /> contact="Ana Trujillo" /> contact="Anabela Domingues" /> contact="Andr Fonseca" /> contact="Ann Devon" /> contact="Annette Roulet" /> contact="Antonio Moreno" /> contact="Aria Cruz" /> contact="Art Braunschweiger" />

Magique non ? Encore plus intressant, surtout pour ceux qui, comme moi, restent rfractaires la logique de Xpath ou XQuery que je nutilise pas assez souvent pour tre laise les quelques fois o jen aurais besoin, il y a bien entendu la possibilit dinterroger un fichier XML et mme de transformer des donnes (un peu comme XSLT). Par exemple analyser une grappe XML, ne prendre que les lments qui nous intressent et crer une liste dinstances de la classe Client ! Dans le code qui suit nous prendrons le fichier ContactsDemo.xml cr ltape prcdente et nous allons linterroger pour crer une liste de string :
public static void Demo4() { XDocument xml = XDocument.Load("ContactsDemo.xml"); var query = from c in xml.Descendants("client") where ((string)c.Attribute("ID")).Contains('M') orderby (string)c.Attribute("contact") descending select (string)c.Attribute("contact")+" ["+ (string)c.Attribute("ID")+"]"; Console.WriteLine("il y a "+query.Count().ToString()+" clients:\n"); foreach (string s in query) Console.WriteLine(s); Console.ReadLine(); }

On remarque dabord quen labsence dun schma la requte LINQ to XML fonctionne en utilisant directement les noms des attributs. Sans schma pas daide sur ces derniers quil faut taper comme des chanes et transtyper convenablement. La sortie console est la suivante :
Le rsultat compte 4 clients: Aria Cruz [FAMIA] Annette Roulet [LAMAI] Alexander Feuer [MORGK] Alejandra Camino [ROMEY]

Page 22

Bluffant non ? LINQ to XML Cest un petit pas pour XML mais un pas de gant pour le dveloppeur ! Les grincheux et les impatients diront peut-tre quon sloigne du typage fort quoffre LINQ puisquici il faut manipuler des balises dont les noms sont saisis en chanes de caractres (risque derreur) et dont les types doivent tre obtenus par transtypage (perte du typage fort). Certes Mais jai une bonne nouvelle pour eux (et pour les autres !), cela sappelle LINQ to XSD, tout simplement. Il y a dj eu plusieurs releases en preview de cette technologie qui avait t initie par le docteur Ralf Lmmel quand il appartenait lquipe LINQ to XML. Ce chercheur tant reparti dans sa Germanie natale pour y professer, ce projet connu quelque retard. Mais trs rcemment Shyam Pather (Microsoft) la confrence XML 2007 de Boston a annonc que tous les efforts seraient faits pour fournir LINQ to XSD, mais dans un second temps, aprs la sortie de Visual Studio 2008. Encore un peu de patience donc et il sera possible de dfrencer les proprits en bnficiant du support Intellisense avec LINQ to XSD.

LINQ to Entities
Jusqu ce point nous avons pu voir luvre LINQ sur des collections, sur des donnes XML , des Datasets et des sources SQL. Ce sont ces dernires qui nous intressent nouveau ici. LINQ to SQL est trs puissant et simple utiliser, mais il sagit dune approche centre sur les donnes. En ralit LINQ to SQL apparat ds lors comme une simplification de lcriture des Data Access Layer en rendant le mapping plus ais. Avec LINQ to Entities les donnes sources ne sont plus des tables ou des objets mapps sur ces dernires, les sources sont purement conceptuelles. Les quipes de LINQ ont russi casser le mur entre langage objet et structure de base de donnes. Un constat navrant simpose : une base de donnes apparat toujours au dveloppeur sous la forme de son schma physique alors quune base bien conue a dabord t murement rflchie par un dveloppeur spcialis ou un analyste. Et ce que ces derniers ont modlis (le mot est lch) cest un MCD, un Modle Conceptuel des Donnes. Ce MCD a pu tre conu dans un style Merise, ou en notation Entit/Relation, sous le forme de diagrammes UML de classes retranscrits en schmas physiques, peu importe loutil et la mthodologie utilise, une base de donnes nat dabord sous les traits dun modle conceptuel et non dun schma physique. La plupart des outils de conception de schmas de base de donnes savent dailleurs produire automatiquement le schma physique (MPD : Modle Physique des Donnes) partir du MCD, jusquau script SQL de cration de la base qui nest quun sous-produit de toute cette dmarche, le ct sale , prosaque, terriblement matriel qui nintresse gure de monde tel point quon dlgue ce travail des automates Et pourtant ! Cest avec ce schma physique, cet avorton du MPD produit par un automate que le dveloppeur dapplications va devoir se frotter en permanence ! Le rle du dveloppeur revient ainsi dfaire le travail de lautomate pour remonter sur le MCD afin de le faire concider avec les concepts, objectivs, de son application. Une tche

Page 23

stupide, seme dembuches et source dinnombrables erreurs (mme si le dveloppeur peut disposer du MCD original de la base de donnes, cela ne simplifie en rien sa tche de programmation des accs aux donnes). Dveloppeur != Plombier Les dveloppeurs ne sont pas des plombiers (quelle que soit par ailleurs la noblesse de ce mtier) et pourtant ils ont la nette sensation quen matire de base de donnes ils passent leur temps abouter, tordre et remodeler de la tuyauterie dans lespoir de la faire concider aux besoin de leurs applications. Une gymnastique usante et source derreurs entre un espace rectangulaire, celui des SGBD, et la logique des graphes et des objets propre aux langages modernes. Cette sensation dtre tout le temps en train de bricoler est, au-del du constat technique navrant, quelque chose de peu gratifiant. Et quand un dveloppeur ne se sent pas gratifi par ce quil fait il le fait mal. Changer de dimension Il faut donc trouver un moyen dchapper la logique en 2D des rectangles du docteur Codd. Lorsquon regarde un diagramme de classes dune application objet bien conue, on saperoit quil ny a que fort peu de diffrences avec un MCD de base de donnes. Au dpart ces deux mondes sont finalement trs proches ! Le divorce nest quune apparence, un terrible coup du sort : le concepteur de la base est oblig dabaisser le niveau conceptuel de son schma celui dun schma physique pour satisfaire les exigences du SGBD cible. Le dveloppeur dapplications ne voyant que ce rsultat Il faut ainsi trouver un moyen pour que la base de donnes puisse rapparatre sous une forme conceptuelle afin que le dveloppeur ne sencombre plus des dtails et limitations du modle physique optimis pour le moteur de base de donnes et non pour un humain ou une application. On pourrait supposer crer un outil qui, partant dun diagramme de classes, produirait dun ct le schma physique de la base de donnes et, de lautre, le mapping vers les objets en mmoire. En fait cela existe dj mais noffre finalement pas la souplesse attendue. En effet, une base de donnes rpond des critres normatifs issus des rgles de Codd, et rien ne permet dy chapper. Un bon designer de base de donnes, mme lorsquil utilise un outil de conceptualisation, aura toujours tendance crer des entits et des relations qui bien que modlisant parfaitement les besoins de lutilisateur resteront empreintes dune certaine logique rectangulaire . Par exemple lhritage nexiste pas dans les bases SQL, mme si certains outils permettent cette notation. Ne parlons pas du support des interfaces, des class helpers, etc De plus une base de donnes ne contient que des donnes ! Il manque toute la logique des actions (mthodes), lordre des squences, bref tout ce qui fait quune application est autrement plus complexe quune base de donnes. Cette dynamique particulire entre donnes et actions est le propre de la programmation objet, certainement pas de la cration des bases de donnes ni mme de leur modlisation et encore moins de leur optimisation (qui impose souvent de dgrader encore plus le

Page 24

schma conceptuel et le modle physique par des processus barbares nomms dnormalisation ). De fait, obliger le partage dun mme schma conceptuel pour la base de donnes et lapplication est une fausse bonne ide. Retour la case dpart. La solution consiste finalement laisser les designers de base de donnes exercer au mieux leur art, et laisser les dveloppeurs exercer le leur. Cest le principe de LINQ to Entities : partir du schma physique de la base de donnes le dveloppeur va pouvoir crer un modle conceptuel collant parfaitement aux besoins de son application. Mieux, il pourra crer autant de modles diffrents sur un mme schma physique en fonction des concepts qui sont manipuls par telle ou telle autre applications. LINQ to Entities est ainsi une version de LINQ qui permet dcrire des requtes non plus en direction dune base de donnes et de son schma physique, mais bien en direction dun modle totalement conceptuel qui va, en quelque sorte, faire cran entre la base de donnes et les objets de lapplication. Charge LINQ de traduire en SQL tout ce que le dveloppeur fera avec les instances des classes dcrites dans le modle. Passons la ralit Il tait impossible de parler de LINQ sans quelques digressions sur les concepts en jeu tellement ils sont essentiels et lourds de consquences. Mais dans cet article le moment est venu de passer un exemple concret ! Avertissement : Ce que nous allons voir maintenant est en ralit une extension de Linq to SQL. Dans ce mode on dispose dun modle, dun concepteur visuel spcifique et dune gnration automatiquement de code. Mais en ralit on reste encore en Linq to SQL. Le vritable Linq to Entities repose sur lEntity Framework. Les nuances visuelles pour la dmo sont faibles, surtout pour un tour dhorizon , ce quest le prsent article. Le vritable Linq to Entities est encore en bta (la 3 est disponible sur MSDN lheure o jcris ce texte), les diffrences semblent mineures mais sont essentielles techniquement. Linq to Entities repose sur un mapping XML et non sur des attributs, il permet aussi quune entit du modle soit mappe sur plusieurs tables ou vues ce qui est impossible avec Linq to SQL. La dmo qui suit utilise ainsi le mode visuel de Linq to SQL pour faire comprendre ce quest Linq to Entities, en raison de cette proximit visuelle et pratique et aussi parce que ce mode de fonctionnement de Linq to SQL est dj en soi une petite rvolution. Mais techniquement, vous laurez compris, Linq to Entities et lEntity Framework vont encore plus loin, mme si pour linstant ce nest quune bta (prsentation sur MSDN : http://msdn2.microsoft.com/en-us/library/bb896679.aspx). Tout dabord, LINQ to Entities ne rclame au dpart rien de spcial, on peut donc lutiliser dans tout type de projet. Pour crer le modle conceptuel, appel EDM (Entity Data Model, modle de donnes des entits), il suffit, au sein dun projet existant, dajouter un nouvel lment et de choisir dans le menu propos ADO.NET Entity Model . LEntity Framework tant encore en bta test (voir lavertissement plus haut) nous utiliserons LINQ to SQL classes (voir la figure ci-dessous). Il y a trs peu de diffrences visuelles et nous reviendrons dans un autre article sur les vritables EDM de lEntity Framework. Considrez ici que le principe est le mme, la principalement nuance tant que Linq to Entities et lEntity Framework forment une solution totalement objet autorisant un

Page 25

mapping XML trs libre et bea aucoup moi ins limit que celui des classes Linq to SQL de d la r e dtail su en ur lEntity Framework F Entities dan ns un dmo qui suit. Je reviendrai et Linq to E article venir.

Figure 1 - Cration d'u un diagramme e de classes Linq L to SQL

Figur re 2 - Cratio on d'un diagra amme de clas sses ADO.NET Entity Data M Model

Page 26

Constru uire le mod dle La construction du un modle seffectue de faon to otalement libre, l comm me un diagra amme ses UML et avec des ou utils trs pr roches. Il es st donc possible, parta ant de rien, , de de class concevo oir un mod le correspo ondant exac ctement au u besoin de lapplicatio on et dans un second temps de mapper m les classes ou proprits p des tables s et champs s dune bas se de donnes s. Les classes Linq to SQL S utilise es pour cett te dmo na autorisent q quun mapp ping 1:1 (1 classe = 1 table / vue). Lin nq to Entitie es et lEntit ty Framewo ork permett tent eux le d ant les possibilits et renforant r l dcoupla le age avec le modle mapping multiple dmultiplia ue de la bas se de donn es. physiqu Il existe e aussi une faon plus simple de procder p qu ue je vais utiliser ici, il suffit de partir p dune connexion avec une base de donn es et de piocher p par dragn drop p les tables s quon dsire adresser a puis de modif fier le mod le pour le faire tendre vers le niveau dabstraction de lapp plication (le es modles ADO.NET Entity E mode els disposen nt dun wiza ard permett tant de construire un diagr ramme dep puis une bas se de donn es automatiquement) ). ant de la ba ase Northw wind et en s lectionnan nt les tables s Customers et Orders nous En parta arrivons s une prem mire bau uche de mod dle. On no otera que le e designer v visuel des classes Linq to SQL dispose e dune too olbox perme ettant dajo outer des classes, des association ns et des hritage es. On retro ouve les m mes foncti ions dans le es modles Entity Fram mework, sau uf que le conce ept de class se est remp plac par ce elui dentit.

Fig gure 3 - Un modle m de clas sses Linq to SQL S

Page 27

Figure 4 - Un diagramme EDM, Entity Data Model

Le diagramme prsent figure 4 montre le concepteur visuel des EDM. On remarque que si les outils et la prsentation sont similaires, on dispose, sous le diagramme, dun onglet spcifique permettant de grer le mapping. Cest cet outil l plus quun autre qui, dun point de vue pratique, change tout puisquil permet de manipuler le mapping librement l o les modles de classes Linq to SQL ne le permettent pas. Au passage vous remarquerez sur le schma de la figure 4 des relations many-to-many qui ne sont pas prises en charges par les classes Linq to SQL. A ce stade prcis notre modle de la figure 3 est brut de fonderie il calque 100% le modle physique puisque nous venons justement de partir de ce dernier. On remarque que par exemple la jointure entre clients et commandes a t automatiquement dduite de celle existante dans la base de donnes. On remarque aussi que le designer visuel a cr deux classes, enlevant automatiquement au passage le s final de Customers et Orders Les proprits de la jointure sont les suivantes :

Page 28

Figur re 5 - Propri t d'une rela ation (Linq to o SQL)

Figure 6 - La mme relation exprime dans un Entity Data Mode el

ation entre deux classe es, comme dans un dia agramme de e Il sagit ici de dfinir une rela n plus une jointure SQL L. Notre exemple sappuie sur Lin nq to SQL comme classes UML et non indiqu dans lavertissement dintroduct tion mais nous continu uons prse ente,r quan nd cela est poss sible, un pa arallle avec lEntity Framework. F On remarq que ainsi les s petites nu uances dans la dfinition dune d relation entre la a figure 5, modle m de classes Linq q to SQL, et t la figure 6, 6 modle de d type Enti ity Data Model.

Page 29

Personn naliser le modle m De fao on trs simp ple il est possible dajo outer ou de e supprimer des relatio ons, des tab bles, denlev ver ou dajo outer des pr roprits au ux entits, etc Il est mme possib ble de modif fier le mapping des ch hamps, dinspecter et de changer r le SQL a gnr pa ar LINQ et mme m dindiquer ce dernier d quil faut non pas produir re du qui sera SQL pou ur accder aux a donne es mais utili iser une ou plusieurs procdures p stockes (slection et mise jour nota amment). Les L possibili its les plus s avances ne sont disponibles qu ue dans un modle de type e Entity Dat ta Model. Le es modles de classes Linq to SQL L restent pl lus d mapping notamment. limits en terme de st une techn nologie simple mais te ellement ric che quil est t bien vide emment LINQ to Entities es p article den fai ire le tour complet c et en dtail. Dautant qu ue cette hors du cadre du prsent technologie ainsi que q lEntity y Framework ne sont pour linstan nt quen bta. Il est pl lus simple de d continue er notre dm monstration n en se basant sur les modles de e classes Lin nq to SQL, tec chnologie in ntgre Visual V Studio o 2008 et utilisable u to out de suite en product tion. Pour illu ustrer le pr ropos sans entrer e dans la complex xit dune vritable v ap pplication, apporto ons quelque es modificat tions symboliques notre mod dle de classes Linq to o SQL en supprim mant des pro oprits et en complmentant les entits du u modle pa ar du code personn nalis (on pourrait alle er encore pl lus loin dan ns un modl le EDM). En prem mier lieu no ous ne conse ervons que quelques in nformations s pour chaq que entit, ce qui produit le diagram mme suivant t:

Dans un n second tem mps nous allons ajoute er du code chaque entit. Un clic droit et voir le code affiche lditeur de e code plac sur la dfinition de lentit. On remarque que tout le code autom matique est cach dans s un autre fichier f et que les class ses prsent es sont classes partielles). Pou ur faire simp ple modifio ons les mth hodes ToSt tring() de e vides (c chaque entit :

Page 30

namespace LINQEDemo { partial class Customer { public override string ToString() { return "Socit "+CompanyName; } } partial class Order { public override string ToString() { return "Cde n " + OrderID + " Pour: " + Customer.CompanyName; } }

On imagine bien quil est possible dajouter tout le code fonctionnel dont on a besoin, on est en C# dans du code normal et bnficiant de lensemble des possibilits du Framework. Nous nous arrterons ici pour les personnalisations. Premier test du modle Pour tester notre modle et ses personnalisations, le plus simple est de demander une fiche client et une fiche commande, isolment, et den faire un affichage en chanes de caractres (ce qui appellera les mthodes ToString surcharges) :
public static void Demo1() { using (NorthwindDataContext db = new NorthwindDataContext()) { Console.WriteLine(db.Orders.First()); Console.WriteLine(db.Customers.First()); Console.ReadLine(); } }

La sortie console nous montre alors :


Cde n 10248 Pour: Vins et alcools Chevalier Socit Alfreds Futterkiste

La premire ligne affiche la premire commande de la base de donnes mise en forme par notre surcharge de ToString(). Il en va de mme pour le premier client. Au passage on remarque que la technique est similaire LINQ to SQL puisque nous utilisons un DataContext personnalis. La seule diffrence est que ce dernier est gnr automatiquement par le designer de Visual Studio. Cest bien parce que nous sommes toujours sous LINQ to SQL la vritable rvolution se trouve dans lEntity Framework et Linq to Entities que avons survol ici. Sous Entity Framework, un EDM donne naissance un ObjectContext et non plus un DataContext. La diffrence semble mineure, mais elle est techniquement profonde et ouvre des possibilits bien plus grandes que celles de Linq to SQL.

Page 31

Ajout dune d proc dure stock ke au mod dle Par drag gn drop il est e possible e de dpose er sur un mo odle de classes Linq t to SQL des procdu ures stocke es depuis le e gestionnaire de serve eurs vers un n espace ve ertical coll au modle. Cela ajoute la PS ce c dernier en e tant que mthode du d DataCon ntext personn nalis Ajou utons ainsi la PS Ten_ _Most_Expensive_Pr roducts e et interrogeons-l pour tes ster le fonc ctionnemen nt de lensemble :

Figure 7 - L'ajout d'une procdure e stocke un modle

On voit sur limage e ci-dessus la procdur re stocke ajoute dan ns la colonn ne de droite e, colle au a modle (les ( procdu ures stock es sont gr res un peu diffremm ment sous En ntity Framew work). Le code e pour activ ver cette m thode est on ne peut t plus simple e (la classe e ObjectDu umper est la mme m que celle c utilise e plus avant dans cet article) a :
using (NorthwindDataContext db = new NorthwindDataContext()) { ObjectDumper.W Write(db.Ten_Most_ _Expensive_Products()); Console.ReadLine(); }

Code qu ui produit la a sortie con nsole suivan nte :


TenMost tExpensiveP Products=C te de Blay ye UnitPri ice=263,500 00 TenMost tExpensiveP Products=Th hringer Ro ostbratwurs st U UnitPrice=1 123,7900 TenMost tExpensiveP Products=Mi ishi Kobe Niku N UnitPrice e=97,0000 TenMost tExpensiveP Products=Si ir Rodney's s Marmalade e U UnitPrice=8 81,0000 TenMost tExpensiveP Products=Ca arnarvon Ti igers UnitPrice e=62,5000 TenMost tExpensiveP Products=Ra aclette Cou urdavault UnitPrice e=55,0000 TenMost tExpensiveP Products=Ma anjimup Dri ied Apples UnitPrice e=53,0000 TenMost tExpensiveP Products=Ta arte au suc cre UnitPrice e=49,3000 TenMost tExpensiveP Products=Ip poh Coffee ice=46,0000 0 UnitPri TenMost tExpensiveP Products=R ssle Sauer rkraut UnitPrice e=45,6000

On peut t de la mm me faon ajouter et g rer des PS qui attende ent des para amtres. Cest le cas dans la base de e donnes Northwind N d la procdure CustO de OrderHist t qui, une fois f ajoute e au modle e, donnera naissance une mtho ode de mm me nom attendant en param tre un ID cl lient.

Page 32

using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; ObjectDumper.Write(db.CustOrderHist("ALFKI")); Console.ReadLine(); }

Nous avons ajout ici la sortie de la trace SQL sur la console. De fait, pour le client indiqu la sortie console est la suivante (avec en premier le SQL produit par LINQ) :
EXEC @RETURN_VALUE = [dbo].[CustOrderHist] @CustomerID = @p0 -- @p0: Input NChar (Size = 5; Prec = 0; Scale = 0) [ALFKI] -- @RETURN_VALUE: Output Int (Size = 0; Prec = 0; Scale = 0) [Null] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

ProductName=Aniseed Syrup Total=6 ProductName=Chartreuse verte Total=21 ProductName=Escargots de Bourgogne Total=40 ProductName=Flotemysost Total=20 ProductName=Grandma's Boysenberry Spread Total=16 ProductName=Lakkalikri Total=15 ProductName=Original Frankfurter grne Soe Total=2 ProductName=Raclette Courdavault Total=15 ProductName=Rssle Sauerkraut Total=17 ProductName=Spegesild Total=2 ProductName=Vegie-spread Total=20

Nous disposons ainsi de lhistorique des commandes dun client sous la forme dune simple mthode du DataContext et notre application peut utiliser directement le rsultat sous la forme dune collection type. Utiliser les relations Il est temps dinterroger le modle en exploitant les relations quil dfinit. Voici une requte un petit plus complexe :
public static void Demo4() { using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; var query = (from c in db.Customers select new { c.CompanyName, c.City, Orders = from o in c.Orders where o.ShipCode == 2 select new { o.OrderID, o.ShipCode } }).Take(5); ObjectDumper.Write(query,2); Console.ReadLine(); } }

Explications : nous demandons ici LINQ de nous fournir une liste constitue dlments dont le type, anonyme, est form de trois proprits : le nom de la socit du client, la

Page 33

ville du client et une proprit Orders qui est elle-mme une liste. Cette liste est constitue elle-aussi dlments de type anonyme possdant deux proprits, lID de la commande et le mode de livraison. La proprit Orders est fabrique sur la base dune sous-requte LINQ filtrant les commandes de chaque client et ne retenant que celles ayant un mode de livraison gal au code 2. Enfin, nous ne prenons que les 5 premiers clients de la liste globale (par lopration Take(5) applique toute la requte mise entre parenthses). Je vous fais grce de la sortie console qui montre outre le rsultat attendu les diverses requtes SQL envoyes la base de donnes par LINQ (grce db.Log redirige vers la console). Crer des relations Si nous ajoutons la table Suppliers (fournisseurs) notre modle nous disposons dune entit de plus dans ce dernier mais aucune relation nest dduite de la base de donnes. En effet, il nexiste aucune jointure entre commandes et fournisseurs ni entre ces derniers les clients. Il existe bien entendu dans la base de donnes un chemin reliant toutes ces entits en passant par les lignes de commandes et les articles. Mais dautres chemins sont envisageables. De plus, nous travaillons sur un modle conceptuel propre notre application, nous ne sommes que trs peu intresss par la ralit de lorganisation de la base de donnes. Notamment, nous souhaitons ici obtenir une liste contenant pour chaque client la liste des fournisseurs se trouvant dans la mme ville. Cela na pas de sens pour la base de donnes telle quelle a t conue, mais cela en a un pour nous, ponctuellement dans notre application (par exemple tudier comment faire livrer les clients directement par les fournisseurs les plus proches au lieu de faire transiter la marchandise par notre stock). Nous allons pour cela crer une relation la vole dans une requte :
using (NorthwindDataContext db = new NorthwindDataContext()) { var query = from c in db.Customers join s in db.Suppliers on c.City equals s.City into sups where sups.Count() > 0 select new { c.CompanyName, c.City, Suppliers = sups }; ObjectDumper.Write(query, 2); Console.ReadLine(); }

La sortie console ne vous ferait rien voir de plus, mais regardons le code SQL gnr et mditez sur la diffrence de lisibilit entre les quelques lignes LINQ ci-dessus et ce pav SQL ci :

Page 34

SELECT [t0].[CompanyName], [t0].[City], [t1].[SupplierID], [t1].[CompanyName] AS [CompanyName2], [t1].[ContactName], [t1].[ContactTitle], [t1].[Address], [t1].[ City] AS [City2], [t1].[Region], [t1].[PostalCode], [t1].[Country], [t1].[Phone] , [t1].[Fax], [t1].[HomePage], ( SELECT COUNT(*) FROM [dbo].[Suppliers] AS [t3] WHERE [t0].[City] = [t3].[City] ) AS [value] FROM [dbo].[Customers] AS [t0] LEFT OUTER JOIN [dbo].[Suppliers] AS [t1] ON [t0].[City] = [t1].[City] WHERE (( SELECT COUNT(*) FROM [dbo].[Suppliers] AS [t2] WHERE [t0].[City] = [t2].[City] )) > @p0 ORDER BY [t0].[CustomerID], [t1].[SupplierID] -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

Je vous laisse faire vos propres dductions Modifications des donnes Interroger de faon simple et contrle les donnes est une chose, que LINQ to Entities fait fort bien, mais quen est-il de la modification des donnes ? Si vous vous attendez des tonnes de code vous allez tre dus ! Modifions une fiche client pour commencer :
public static void Demo6() { using (NorthwindDataContext db = new NorthwindDataContext()) { var cust = db.Customers.First(c => c.CustomerID == "PARIS"); cust.PostalCode = "75016"; db.SubmitChanges(); } }

Nous obtenons le client dont lID est PARIS , nous modifions son code postal puis nous sauvegardons les modifications dans la base de donnes Plus simple je nai pas en stock. En revanche je peux vous montrer le code SQL gnr par LINQ pour ces trois petites lignes de C# :
SELECT TOP (1) [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[City], [t0].[PostalCode], [t0].[Country] FROM [dbo].[Customers] AS [t0] WHERE [t0].[CustomerID] = @p0 -- @p0: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [PARIS] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8 UPDATE [dbo].[Customers] SET [PostalCode] = @p6 WHERE ([CustomerID] = @p0) AND ([CompanyName] = @p1) AND ([ContactName] = @p2) AND ([City] = @p3) AND ([PostalCode] = @p4) AND ([Country] = @p5) -- @p0: Input NChar (Size = 5; Prec = 0; Scale = 0) [PARIS] -- @p1: Input NVarChar (Size = 17; Prec = 0; Scale = 0) [Paris spcialits] -- @p2: Input NVarChar (Size = 14; Prec = 0; Scale = 0) [Marie Bertrand]

Page 35

------

@p3: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Paris] @p4: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [75012] @p5: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [France] @p6: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [75016] Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

Personnellement jai une petite prfrence pour la version LINQ. Pas vous ? Il est bien entendu possible de continuer comme a trs longtemps avant davoir fait le tour de LINQ. Par exemple ajouter une commande un client (il suffit de crer un objet Order, de linitialiser, de lajouter par Customer.Add() lobjet client et de sauvegarder les changements). LINQ to Entities propose des mcanismes autrement sophistiqus notamment dans le tracking des modifications faites aux objets et lidentification de ces derniers. Car bien entendu les grappes ou les objets isols que nous rcuprons napparaissent que comme de simples instances pour le dveloppeur, mais au moment de mettre jour les donnes comment LINQ sait-il que ces objets proviennent bien de la base de donnes, que le client 1256 dont on a chang lID (cela arrive) est bien le 1256 dans la base (alors que nous navons pas nous-mmes conserv lancienne valeur) ? etc, etc Dautres questions du mme type se posent lorsquil sagit denvoyer un objet en dehors de la machine (par du remoting par exemple) puis de le rcuprer modifi et dappliquer les changements la base de donnes. On pourra aussi se demander comment les transactions sont gres par LINQ. Tout cela a bien entendu des rponses, mais nous dpasserions largement les limites de cet article de prsentation dont les proportions ont explos au fil de ce voyage que je voyais un petit plus court au dpart Nhsitez pas consulter mon blog, certains billets rpondent aux questions souleves ici et dautres rponses viendront sajouter avec le temps comme, bien entendu, un article totalement consacr Linq to Entities et lEntity Framework.

Conclusion
Jai bien cru ne jamais atteindre ce mot magique ! Conclusion. Il est effectivement temps de conclure. LINQ est une desse polymorphe dont on ne saurait se lasser de dcrire les courbes gracieuses, mais il nest de bonne compagnie qui ne se quitte, alors je vous dis bientt pour dautres articles qui visiterons plus en dtail dautres aspects de Visual Studio 2008 et du Framework 3.5 Comme je le conclue souvent sur mon blog : Stay tuned !

Page 36

Vous aimerez peut-être aussi