Académique Documents
Professionnel Documents
Culture Documents
Introduction À LINQ
Introduction À LINQ
IntroductionLINQ
Accueil
Cours
Apprenez dvelopper en C#
Introduction LINQ
Apprenez dvelopper en C#
Licence
Facile
C# avanc
Devenez
Premium
INTRODUCTION LINQ
LINQ signifie Language INtegrated Query. C'est un ensemble d'extensions du langage permettant de faire des requtes sur des donnes en faisant abstraction de leur type. Il permet d'utiliser facil
un jeu d'instructions supplmentaires afin de filtrer des donnes, faire des slections, etc. Il existe plusieurs domaines d'applications pour LINQ :
Linq To Entities ou Linq To SQL qui utilisent ces extensions de langage sur les bases de donnes.
Linq To XML qui utilise ces extensions de langage pour travailler avec les fichiers XML.
Linq To Object qui permet de travailler avec des collections d'objets en mmoire.
L'tude de LINQ ncessiterait un livre en entier, aussi nous allons nous concentrer sur la partie qui va le plus nous servir en tant que dbutant et qui va nous permettre de commencer travailler
simplement avec cette nouvelle syntaxe, savoir Linq To Object. Il s'agit d'extensions permettant de faire des requtes sur les objets en mmoire et notamment sur toutes les listes ou collections.
fait, sur tout ce qui implmente IEnumerable<>.
Besoind'aide?
Ce using est en gnral inclus par dfaut lorsquon cre un nouveau fichier.
Jusqu maintenant, si nous voulions afficher les entiers dune liste dentiers qui sont strictement suprieur 5, nous aurions fait :
1
2
3
4
5
6
7
8
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
foreach(intiinliste)
{
if(i>5)
{
Console.WriteLine(i);
}
}
Grce Linq To Object, nous allons pouvoir filtrer en amont la liste afin de ne parcourir que les entiers qui nous intressent, en faisant :
1
2
3
4
5
6
7
8
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requeteFiltree=fromiinliste
wherei>5
selecti;
foreach(intiinrequeteFiltree)
{
Console.WriteLine(i);
}
Qui donnera :
6
9
15
8
Nous avons ici cr une requte Linq qui contient des mots-cls, comme from, in, where et select. Ici, cette requte veut dire que nous partons (from) de la liste liste en analysant chaque en
la liste dans (in) la variable i o (where) i est suprieur 5. Dans ce cas, il est slectionn comme faisant partie du rsultat de la requte grce au mot-cl select, qui fait un peu office de
Cette requte renvoie un IEnumerable<int>. Le type gnrique est ici le type int car cest le type de la variable i qui est retourne par le select. Le fait de renvoyer un IEnumerable<>
potentiellement de rutiliser le rsultat de cette requte pour un filtre successif ou une expression diffrente. En effet, Linq travaille sur des IEnumerable<> Nous pourrions par exemple ordon
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
1/8
16/2/2015
IntroductionLINQ
cette liste par ordre croissant grce au mot-cl orderby. Cela donnerait :
1
2
3
4
5
6
7
8
9
10
11
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requeteFiltree=fromiinliste
wherei>5
selecti;
IEnumerable<int>requeteOrdonnee=fromiinrequeteFiltree
orderbyi
selecti;
foreach(intiinrequeteOrdonnee)
{
Console.WriteLine(i);
}
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requete=fromiinliste
wherei>5
orderbyi
selecti;
foreach(intiinrequete)
{
Console.WriteLine(i);
}
Et nous aurons :
6
8
9
15
Lintrt est que grce ces syntaxes, nous pouvons combiner facilement plusieurs filtres et construire des requtes plus ou moins complexes.
Par exemple, imaginons des clients :
1
2
3
4
5
6
publicclassClient
{
publicintIdentifiant{get;set;}
publicstringNom{get;set;}
publicintAge{get;set;}
}
que lon souhaiterait savoir majeurs, puis tris par Age puis par Nom, nous pourrions faire :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Client>listeClients=newList<Client>
{
newClient{Identifiant=1,Nom="Nicolas",Age=30},
newClient{Identifiant=2,Nom="Jrmie",Age=20},
newClient{Identifiant=3,Nom="Delphine",Age=30},
newClient{Identifiant=4,Nom="Bob",Age=10}
};
IEnumerable<string>requete=fromclientinlisteClients
whereclient.Age>18
orderbyclient.Age,client.Nom
selectclient.Nom;
foreach(stringprenominrequete)
{
Console.WriteLine(prenom);
}
Ce qui donnera :
Jrmie
Delphine
Nicolas
Notez ici que mon select renvoie le nom du client, qui est un string. Il est donc normal que ma requte renvoie un IEnumerable<string> car jai choisi quelle ne slectionne que les nom
sont de type string.
Il est assez frquent de construire des objets anonymes lintrieur dune requte. Par exemple, plutt que de renvoyer uniquement le nom du client, je pourrais galement renvoyer lAge
je nai pas besoin de lidentifiant, je peux crer un objet anonyme juste avec ces deux proprits :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
List<Client>listeClients=newList<Client>
{
newClient{Identifiant=1,Nom="Nicolas",Age=30},
newClient{Identifiant=2,Nom="Jrmie",Age=20},
newClient{Identifiant=3,Nom="Delphine",Age=30},
newClient{Identifiant=4,Nom="Bob",Age=10},
};
varrequete=fromclientinlisteClients
whereclient.Age>18
orderbyclient.Age,client.Nom
selectnew{client.Nom,client.Age};
foreach(varobjinrequete)
{
Console.WriteLine("{0}a{1}ans",obj.Nom,obj.Age);
}
Et nous aurons :
Jrmiea20ans
Delphinea30ans
Nicolasa30ans
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
2/8
16/2/2015
IntroductionLINQ
Mon objet anonyme contient ici juste une proprit Nom et une proprit Age. noter que je suis oblig ce moment-l dutiliser le mot-cl var pour dfinir la requte, car je nai pas de type don
cette requte. De mme, dans le foreach je dois utiliser le mot-cl var pour dfinir le type anonyme.
Les requtes peuvent tre de plus en plus compliques, comme faisant des jointures. Par exemple, rajoutons une classe Commande :
1
2
3
4
5
6
publicclassCommande
{
publicintIdentifiant{get;set;}
publicintIdentifiantClient{get;set;}
publicdecimalPrix{get;set;}
}
List<Client>listeClients=newList<Client>
{
newClient{Identifiant=1,Nom="Nicolas",Age=30},
newClient{Identifiant=2,Nom="Jrmie",Age=20},
newClient{Identifiant=3,Nom="Delphine",Age=30},
newClient{Identifiant=4,Nom="Bob",Age=10},
};
List<Commande>listeCommandes=newList<Commande>
{
newCommande{Identifiant=1,IdentifiantClient=1,Prix=150.05M},
newCommande{Identifiant=2,IdentifiantClient=2,Prix=30M},
newCommande{Identifiant=3,IdentifiantClient=1,Prix=99.99M},
newCommande{Identifiant=4,IdentifiantClient=1,Prix=100M},
newCommande{Identifiant=5,IdentifiantClient=3,Prix=80M},
newCommande{Identifiant=6,IdentifiantClient=3,Prix=10M},
};
Et grce une jointure, rcuprer avec ma requte le nom du client et le prix de sa commande :
1
2
3
4
5
6
7
8
varliste=fromcommandeinlisteCommandes
joinclientinlisteClientsoncommande.IdentifiantClientequalsclient.Identifiant
selectnew{client.Nom,commande.Prix};
foreach(varelementinliste)
{
Console.WriteLine("Leclient{0}apay{1}",element.Nom,element.Prix);
}
Ce qui donne :
LeclientNicolasapay150,05
LeclientJrmieapay30
LeclientNicolasapay99,99
LeclientNicolasapay100
LeclientDelphineapay80
LeclientDelphineapay10
On utilise le mot-cl join pour faire la jointure entre les deux listes puis on utilise le mot-cl on et le mot-cl equals pour indiquer sur quoi on fait la jointure.
noter que ceci peut se raliser en imbriquant galement les from et en filtrant sur lgalit des identifiants clients :
1
2
3
4
5
6
7
8
9
varliste=fromcommandeinlisteCommandes
fromclientinlisteClients
whereclient.Identifiant==commande.IdentifiantClient
selectnew{client.Nom,commande.Prix};
foreach(varelementinliste)
{
Console.WriteLine("Leclient{0}apay{1}",element.Nom,element.Prix);
}
Il est intressant de pouvoir regrouper les objets qui ont la mme valeur. Par exemple pour obtenir toutes les commandes, groupes par client, on ferra :
1
2
3
4
5
6
7
8
9
10
11
varliste=fromcommandeinlisteCommandes
groupcommandebycommande.IdentifiantClient;
foreach(varelementinliste)
{
Console.WriteLine("Leclient:{0}aralis{1}commande(s)",element.Key,element.Count());
foreach(Commandecommandeinelement)
{
Console.WriteLine("\tPrix:{0}",commande.Prix);
}
}
Il est possible de cumuler le groupby avec notre jointure prcdente histoire davoir galement le nom du client :
1
2
3
4
5
varliste=fromcommandeinlisteCommandes
joinclientinlisteClientsoncommande.IdentifiantClientequalsclient.Identifiant
groupcommandebynew{commande.IdentifiantClient,client.Nom};
foreach(varelementinliste)
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
3/8
16/2/2015
6
7
8
9
10
11
12
IntroductionLINQ
{
Console.WriteLine("Leclient{0}({1})aralis{2}commande(s)",element.Key.Nom,element.Key.IdentifiantClient,element.Count());
foreach(Commandecommandeinelement)
{
Console.WriteLine("\tPrix:{0}",commande.Prix);
}
}
Et nous obtenons :
LeclientNicolas(1)aralis3commande(s)
Prix:150,05
Prix:99,99
Prix:100
LeclientJrmie(2)aralis1commande(s)
Prix:30
LeclientDelphine(3)aralis2commande(s)
Prix:80
Prix:10
noter que le groupby termine la requte, un peu comme le select. Ainsi, si lon veut slectionner quelque chose aprs le groupby, il faudra utiliser le mot-cl into et la syntaxe suivante :
Ce qui donnera :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
varliste=fromcommandeinlisteCommandes
joinclientinlisteClientsoncommande.IdentifiantClientequalsclient.Identifiant
groupcommandebynew{commande.IdentifiantClient,client.Nom}intocommandesGroupees
select
new
{
commandesGroupees.Key.IdentifiantClient,
commandesGroupees.Key.Nom,
NombreDeCommandes=commandesGroupees.Count()
};
foreach(varelementinliste)
{
Console.WriteLine("Leclient{0}({1})aralis{2}commande(s)",element.Nom,element.IdentifiantClient,element.NombreDeCommandes);
}
Lintrt dutiliser le mot-cl into est galement de pouvoir enchainer avec une autre jointure ou autre filtre permettant de continuer la requte.
Il est galement possible dutiliser des variables lintrieur des requtes grce au mot-cl let. Cela va nous permettre de stocker des rsultats temporaires pour les rutiliser ensuite :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
varliste=fromcommandeinlisteCommandes
joinclientinlisteClientsoncommande.IdentifiantClientequalsclient.Identifiant
groupcommandebynew{commande.IdentifiantClient,client.Nom}intocommandesGroupees
lettotal=commandesGroupees.Sum(c=>c.Prix)
wheretotal>50
orderbytotal
selectnew
{
commandesGroupees.Key.IdentifiantClient,
commandesGroupees.Key.Nom,
NombreDeCommandes=commandesGroupees.Count(),
PrixTotal=total
};
foreach(varelementinliste)
{
Console.WriteLine("Leclient{0}({1})aralis{2}commande(s)pouruntotalde{3}",element.Nom,element.IdentifiantClient,element.NombreDeCommandes,
}
Par exemple, ici jutilise le mot-cl let pour stocker le total dune commande groupe dans la variable total (nous verrons la mthode Sum() un tout petit peu plus bas), ce qui me permet ensui
filtrer avec un where pour obtenir les commandes dont le total est suprieur 50 et de les trier par ordre de prix croissant.
Ce qui donne :
LeclientDelphine(3)aralis2commande(s)pouruntotalde90
LeclientNicolas(1)aralis3commande(s)pouruntotalde350,04
Nous allons nous arrter l pour cet aperu des requtes LINQ. Nous avons pu voir que le C# dispose dun certain nombre de mots-cls qui permettent de manipuler nos donnes de manire trs
puissante mais dune faon un peu inhabituelle.
Cette faon dcrire des requtes LINQ sappelle en anglais la sugar syntax , que lon peut traduire par sucre syntaxique . Il dsigne de manire gnrale les constructions d'un langage qui fac
la rdaction du code sans modifier l'expressivit du langage.
Voyons prsent ce quil y a derrire cette jolie syntaxe.
En fait, toute la sugar syntax que nous avons vue prcdemment repose sur un certain nombre de mthodes dextensions qui travaillent sur les types IEnumerable<T>. Par exemple, la requte s
:
1
2
3
4
5
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requeteFiltree=fromiinliste
wherei>5
selecti;
foreach(intiinrequeteFiltree)
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
4/8
16/2/2015
IntroductionLINQ
6 {
7 Console.WriteLine(i);
8 }
s'crit vritablement :
1
2
3
4
5
6
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requeteFiltree=liste.Where(i=>i>5);
foreach(intiinrequeteFiltree)
{
Console.WriteLine(i);
}
Nous utilisons la mthode dextensions Where() en lui fournissant une expression lambda servant de prdicat pour filtrer la liste.
Cest de cette faon que le compilateur traduit la sugar syntax. Elle nest donc quune faon plus jolie dutiliser ces mthodes dextensions.
Chaque mthode dextension renvoie un IEnumerable<T> ce qui permet denchainer facilement les filtres successifs. Par exemple, rajoutons une date et un nombre darticles notre classe
1
2
3
4
5
6
7
8
publicclassCommande
{
publicintIdentifiant{get;set;}
publicintIdentifiantClient{get;set;}
publicdecimalPrix{get;set;}
publicDateTimeDate{get;set;}
publicintNombreArticles{get;set;}
}
IEnumerable<Commande>commandesFiltrees=listeCommandes.
Where(commande=>commande.Prix>100).
Where(commande=>commande.NombreArticles>10).
OrderBy(commande=>commande.Prix).
ThenBy(commande=>commande.DateAchat);
Nous pouvons obtenir les commandes dont le prix est suprieur 100, o le nombre darticles est suprieur 10, tries par prix puis par date dachat.
De plus, ces mthodes dextensions font beaucoup plus de choses que ce que lon peut faire avec la sugar syntax. Il existe pas mal de mthodes intressantes, que nous ne pourrons pas toutes tu
Regardons par exemple la mthode Sum() (qui a t utilise dans le paragraphe prcdent) qui permet de faire la somme des lments dune liste ou la mthode Average() qui permet de faire l
moyenne :
1 List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
2 Console.WriteLine("Somme:{0}",liste.Sum());
3 Console.WriteLine("Moyenne:{0}",liste.Average());
Dautres mthodes sont bien utiles. Par exemple la mthode dextension Take() nous permet de rcuprer les X premiers lments dune liste :
1 IEnumerable<Client>extrait=listeClients.OrderByDescending(client=>client.Age).Take(5);
Ici, je trie dans un premier temps ma liste par ge dcroissant, et je prends les 5 premiers. Ce qui signifie que je prends les 5 plus vieux clients de ma liste.
Et sil y en a que 3 ? et bien il prendra uniquement les 3 premiers.
Suivant le mme principe, on peut utiliser la mthode First() pour obtenir le premier lment dune liste :
1 List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
2 intpremier=liste.Where(i=>i>5).First();
Il est possible dans ce cas-l dviter une exception avec la mthode FirstOrDefault() qui renvoie la valeur par dfaut du type de la liste (0 si cest un type valeur, null si cest un type rfrenc
1 Clientnicolas=listeClients.FirstOrDefault(client=>client.Nom=="Nicolas");
2 if(nicolas==null)
3 Console.WriteLine("Clientnontrouv");
Ici, je cherche le premier des clients dont le nom est Nicolas. Sil nest pas trouv, alors FirstOrDefault() me renvoie null, sinon, il me renvoie bien sr le bon objet Client.
Dans le mme genre, nous pouvons compter grce la mthode Count() le nombre dlments dune source de donnes suivant un critre :
1 intnombreClientsMajeurs=listeClients.Count(client=>client.Age>=18);
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
5/8
16/2/2015
IntroductionLINQ
Cela me permettra dobtenir une requte contenant les clients majeurs. noter quil y a aura dedans des objets anonymes possdant une proprit Age et une proprit Nom.
Bien sr, nous retrouverons nos jointures avec la mthode dextension Join() ou les groupes avec la mthode GroupBy().
Il existe beaucoup de mthodes dextensions et il nest pas envisageable dans ce tutoriel de toutes les dcrire.
Je vais finir en vous parlant des mthodes ToList() et ToArray() qui comme leurs noms le suggrent, permettent de forcer la requte tre mise dans une liste ou dans un tableau :
1 List<Client>lesPlusVieuxClients=listeClients.OrderByDescending(client=>client.Age).Take(5).ToList();
ou
1 Client[]lesPlusVieuxClients=listeClients.OrderByDescending(client=>client.Age).Take(5).ToArray();
Plutt que davoir un IEnumerable<>, nous obtiendrons cette fois-ci une List<> ou un tableau. Le fait dutiliser ces mthodes dextensions a des consquences que nous allons dcrire.
Excution diffre
Alors, les mthodes dextensions LINQ ou sa syntaxe sucre cest bien joli, mais quel est lintrt de sen servir plutt que dutiliser des boucles foreach, des if ou autres choses ?
Dj, parce quil y a plein de choses dj toutes faites, la somme, la moyenne, la rcupration de X lments, etc.
Mais aussi pour une autre raison plus importante : lexcution diffre.
Nous en avons dj parl, lexcution diffre est possible grce au mot-cl yield. Les mthodes dextensions Linq utilisent fortement ce principe.
Cela veut dire que lorsque nous construisons une requte, elle nest pas excute tant quon itre pas sur le contenu de la requte. Ceci permet de stocker la requte, dempiler ventuellement de
ou des jointures et de ne pas calculer le rsultat tant quon nen a pas explicitement besoin.
Ainsi, imaginons que nous souhaitions trier une liste dentiers, avant nous aurions fait :
1
2
3
4
5
6
7
8
9
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
liste.Sort();
liste.Add(7);
foreach(intiinliste)
{
Console.WriteLine(i);
}
Ce qui aurait affich en toute logique la liste trie puis la fin lentier 7 rajout, c'est--dire :
1
3
4
5
6
8
9
15
7
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
varrequete=liste.OrderBy(e=>e);
liste.Add(7);
foreach(intiinrequete)
{
Console.WriteLine(i);
}
Bien que nous ayons ajout la valeur 7 aprs avoir tri la liste avec OrderBy, on se rend compte que tous les entiers sont quand mme tris lorsque nous les affichons.
En effet, la requte na t excute quau moment du foreach. Ceci implique donc que le tri va tenir compte de lajout du 7 la liste. La requte est construite en mmorisant les conditions com
notre OrderBy, mais cela fonctionne galement avec un where, et tout ceci nest excut que lorsquon le demande explicitement, c'est--dire avec un foreach dans ce cas-l.
En fait, tant que le C# nest pas oblig de parcourir les lments numrables alors il ne le fait pas. Ce qui permet denchainer les ventuelles conditions et viter les parcours inutiles. Par exemple
le cas ci-dessous, il est inutile dexcuter le premier filtre :
1
2
3
4
List<int>liste=newList<int>{4,6,1,9,5,15,8,3};
IEnumerable<int>requete=liste.Where(i=>i>5);
//pleindechosesquin'ontrienvoiraveclarequete
requete=requete.Where(i=>i>10);
car le deuxime filtre a tout intrt tre combin au premier afin dtre simplifi.
Et encore, ici, on nutilise mme pas la requte, il y a encore moins dintrt effectuer nos filtres si nous ne nous servons pas du rsultat.
Ceci peut paratre inattendu, mais cest trs important dans la faon dont Linq sen sert afin doptimiser ses requtes. Ici, le parcours en mmoire pourrait paratre peu couteux, mais dans la mesu
Linq doit fonctionner aussi bien avec des objets, quavec des bases de donnes ou du XML (ou autres), cette optimisation prend tout son sens.
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
6/8
16/2/2015
IntroductionLINQ
Le maitre mot est la performance, primordial quand on accde aux bases de donnes.
Cette excution diffre est garde pour le plus tard possible. C'est--dire que le fait de parcourir notre boucle va obligatoirement entrainer lvaluation de la requte afin de pouvoir retourner les
rsultats cohrents.
Il en va de mme pour certaines autres oprations, comme la mthode Sum(). Comment pourrions-nous faire la somme de tous les lments si nous ne les parcourons pas ?
Cest aussi le cas pour les mthodes ToList() et ToArray().
Par contre, ce nest pas le cas pour les mthodes Where, ou Take, etc
Il est important de connaitre ce mcanisme. Lexcution diffre est trs puissante et connatre son fonctionnement permet de savoir exactement ce que nous faisons et pourquoi nous pourrions
parfois des rsultats tranges.
Oprateur de requte
Excution diffre
Oui
Oprations ensemblistes
Oui
OfType, Where
Oui
Oprations de quantificateur
Non
Oprations de projection
Select, SelectMany
Oui
Oui
Oprations de jointure
Join, GroupJoin
Oui
Regroupement de donnes
GroupBy, ToLookup
Oui
Oprations de gnration
Oui
Oprations d'galit
SequenceEqual
Non
Oprations d'lment
Non
Non
Oprations de concatnation
Concat
Oui
Oprations d'agrgation
Non
Nhsitez pas consulter la documentation de ces mthodes dextensions ou aller voir des exemples sur internet. Il y a beaucoup de choses faire avec ces mthodes. Il est important galemen
bien savoir les matriser afin dviter les problmes de performances. En effet, lvaluation systmatique des expressions peut tre coteuse, surtout quand cest imbriqu dans des boucles.
utiliser judicieusement.
Voil pour ce petit aperu de Linq !
Rappelez-vous bien que Linq est une abstraction qui permet de manipuler des sources de donnes diffrentes. Nous avons vu son utilisation avec les objets implmentant IEnumerable<>
quon appelle Linq To Objects.
Il est possible de faire du Linq en allant manipuler des donnes en base de donnes, on utilisera pour cela Linq To SQL ou Linq To Entity.
De mme, il est possible de manipuler les fichiers XML avec Linq To XML.
Linq apporte des mthodes dextensions et une syntaxe complmentaire afin dtre efficace avec la manipulation de sources de donnes.
Sachez enfin quil est possible de requter nimporte quelle source de donnes partir du moment o un connecteur spcifique a t dvelopp. Cela a t fait par exemple pour interroger Google
Amazon, mais aussi pour requter sur active directory, ou JSON, etc.
En rsum
Linq consiste en un ensemble dextensions du langage permettant de faire des requtes sur des donnes en faisant abstraction de leur type.
Il existe plusieurs domaines d'applications de Linq, comme Linq to Object, Linq to Sql, etc.
La sugar syntax ajoute des mots-cls qui permettent de faire des requtes qui ressemblent aux requtes faites avec le langage SQL.
Derrire cette syntaxe se cache un bon nombre de mthodes d'extension qui tirent parti des mcanismes d'excution diffre.
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
7/8
16/2/2015
IntroductionLINQ
L'auteur
Nicolas
Hilaire
Premium
Premium
eBook
Livre papier
OpenClassrooms
Professionnels
En plus
Suivez-nous
Qui sommes-nous ?
Entreprises
Aider traduire
Le blog OpenClassrooms
Recrutement
Nous contacter
Auteurs
http://openclassrooms.com/courses/apprenezadevelopperenc/introductionalinq
8/8