Quel est l'aspect le plus difficile ou le plus mal compris de LINQ? [fermé]

282

Contexte: Au cours du mois prochain, je donnerai trois conférences sur ou du moins LINQdans le contexte de C#. J'aimerais savoir quels sujets méritent une attention particulière en fonction de ce que les gens peuvent avoir du mal à comprendre ou de ce qu'ils peuvent avoir une impression erronée. Je ne parlerai pas spécifiquement LINQde SQLou de l'Entity Framework, sauf en tant qu'exemples de la façon dont les requêtes peuvent être exécutées à distance à l'aide d'arbres d'expression (et généralement IQueryable).

Alors, qu'est-ce que vous avez trouvé difficile LINQ? Qu'avez-vous vu en termes de malentendus? Les exemples peuvent être les suivants, mais ne vous limitez pas!

  • Comment le C#compilateur traite les expressions de requête
  • Expressions lambda
  • Arbres d'expression
  • Méthodes d'extension
  • Types anonymes
  • IQueryable
  • Exécution différée vs exécution immédiate
  • Streaming vs exécution tamponnée (par exemple OrderBy est différé mais tamponné)
  • Variables locales implicitement typées
  • Lecture de signatures génériques complexes (par exemple Enumerable.Join )
Jon Skeet
la source
3
Je serais intéressé de savoir quand vous allez faire ces discussions, et s'il y a un moyen de les voir en ligne
Mark Heath
2
Première conférence: Copenhague, 30 octobre. J'espère que cela sera enregistré. (Toute la journée!) Deuxième conférence: Londres, 19 novembre au soir, London .NET Users Group, probablement sur Push LINQ. Troisième conférence: lecture, 22 novembre, journée des développeurs, implémentation de LINQ to Objects en 60 minutes.
Jon Skeet
1
Downvoters: veuillez ajouter un commentaire explicatif.
Jon Skeet
2
@Jon, Désolé, mais je dois fermer cela.
Tim Post
3
@Tim: Très bien - de toute façon, il n'obtenait plus de réponses. Personnellement, je pense que cela a fini par être constructif, rappelez-vous - j'ai certainement trouvé utile de voir ce que les gens trouvent difficile. Je ne l'aurais probablement pas demandé maintenant ...
Jon Skeet

Réponses:

271

Exécution retardée

JaredPar
la source
12
Righto - c'est clairement le favori parmi les lecteurs, ce qui est le plus important pour cette question. J'ajouterai également "tampon vs streaming" dans le mix, car cela est étroitement lié - et souvent n'est pas discuté avec autant de détails que j'aimerais voir dans les livres.
Jon Skeet
10
Vraiment? J'ai fait remarquer sa nature paresseuse tant de fois lors de l'apprentissage de Linq, cela n'a jamais été un problème pour moi.
Adam Lassek
26
D'accord avec ALassek. La documentation MSDN indique clairement la nature d'évaluation paresseuse de LINQ. Peut-être que le vrai problème est la nature de programmation paresseuse des développeurs ... =)
Seiti
4
... en particulier lorsque vous réalisez que cela s'applique à LINQ aux objets et pas seulement à LINQ 2 SQL - lorsque vous voyez 10 appels de méthode Web pour récupérer une liste d'éléments lorsque vous êtes déjà en train d'énumérer à travers cette même liste d'éléments et que vous pensiez que la liste a déjà été évaluée
Simon_Weaver
5
Connaître la déclaration de rendement et son fonctionnement est essentiel à mon humble avis pour une compréhension approfondie de LINQ.
peSHIr
125

Je sais que le concept d'exécution différée devrait maintenant me frapper, mais cet exemple m'a vraiment aidé à le comprendre concrètement:

static void Linq_Deferred_Execution_Demo()
{
    List<String> items = new List<string> { "Bob", "Alice", "Trent" };

    var results = from s in items select s;

    Console.WriteLine("Before add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }

    items.Add("Mallory");

    //
    //  Enumerating the results again will return the new item, even
    //  though we did not re-assign the Linq expression to it!
    //

    Console.WriteLine("\nAfter add:");
    foreach (var result in results)
    {
        Console.WriteLine(result);
    }
}

Le code ci-dessus renvoie les éléments suivants:

Before add:
Bob
Alice
Trent

After add:
Bob
Alice
Trent
Mallory
DSO
la source
2
blogs.msdn.com/b/charlie/archive/2007/12/09/… <- Je pense que c'est le meilleur blog pour l'expliquer à mon avis. (loin en 2007, je ne peux pas croire que ça fait déjà si longtemps)
Phill
104

Qu'il y ait plus que LINQpour SQLet les caractéristiques sont plus qu'un simple SQLanalyseur intégré dans la langue.

smaclell
la source
6
J'en ai marre de tout le monde qui pense que: /
TraumaPony
40
Tout le monde ne le fait pas! Je ne sais toujours pas ce qu'est LINQ to SQL, et j'utilise LINQ tout le temps.
Robert Rossney
2
Je suis tellement ennuyé quand j'essaie d'expliquer quelque chose en utilisant LINQ et l'autre personne me regarde et dit "ohhh je n'utilise pas LINQ pour quelque chose comme ça, seulement SQL" :(
Nathan W
13
D'accord, beaucoup de gens ne semblent pas comprendre que LINQ est un outil à usage général.
Matthew Olenik
86

Notation Big O . LINQ facilite incroyablement l'écriture d'algorithmes O (n ^ 4) sans s'en rendre compte, si vous ne savez pas ce que vous faites.

erikkallen
la source
16
Que diriez-vous d'un exemple?
Hughdbrown
4
À titre d'exemple, il veut peut-être dire qu'il est très facile d'avoir une clause Select contenant de nombreux opérateurs Sum (), chacun d'eux provoquant un autre passage sur l'ensemble du jeu d'enregistrements.
Rob Packwood
1
En fait, il pourrait même être utile de parcourir ce qu'est la grande notation O et pourquoi elle est importante, ainsi que quelques exemples de requêtes résultantes inefficaces. Je pense que c'est ce que l'affiche originale suggérait, mais j'ai pensé que je le mentionnerais de toute façon. - EDIT: vient de réaliser que ce post avait 1,5 ans :-)
zcrar70
7
Ce ne serait pas O (n ^ x), ce serait O (xn), qui est juste O (n).
Malfist
3
Essayer de faire une jointure sans l'opérateur de jointure entraînera O (n ^ x): de i1 dans la plage1 de i2 dans la plage2 de i3 dans la plage3 de i4 dans la plage4 où i1 == i2 && i3 == i4 sélectionnez nouveau {i1, i2, i3, i4}. Et j'ai déjà vu cela écrit auparavant. Cela fonctionne, mais très lentement.
MarkPflug
55

Je pense que le fait qu'une Lambdaexpression peut se résoudre à la fois à une arborescence d'expression et à un délégué anonyme, vous pouvez donc transmettre la même lambdaexpression déclarative aux IEnumerable<T>méthodes d' IQueryable<T>extension et aux méthodes d'extension.

Tim Jarvis
la source
2
D'accord. Je suis un vétéran et je viens de réaliser que ce casting implicite avait lieu alors que je commençais à écrire mon propre QueryProvider
TheSoftwareJedi
53

Ça m'a pris façon trop longtemps pour se rendre compte que de nombreuses méthodes d'extension LINQ tels que Single(), SingleOrDefault()etc ont lambdas qui prennent les surcharges.

Tu peux faire :

Single(x => x.id == id)

et je n'ai pas besoin de le dire - ce qu'un mauvais tutoriel m'a donné l'habitude de faire

Where(x => x.id == id).Single()
Simon_Weaver
la source
+1, très agréable. Je m'en souviendrai.
Pretzel
4
J'oublie toujours ça aussi. C'est également vrai Count(), entre autres. Savez-vous s'il existe une différence de performances, en plus du bonus évident de lisibilité du code?
Justin Morgan
1
À l'université, mon professeur voulait décoller les points d'utilisation de ces surcharges !! Je lui ai donné tort!
TDaver
12
Cela peut sembler étrange mais je préfère la deuxième syntaxe. Je le trouve plus lisible.
Konamiman
40

Dans LINQ to SQL, je vois constamment des gens qui ne comprennent pas le DataContext, comment il peut être utilisé et comment il doit être utilisé. Trop de gens ne voient pas le DataContext pour ce qu'il est, un objet Unité de travail, pas un objet persistant.

J'ai vu de nombreuses fois où les gens essaient de singleton un DataContext / session it / etc plutôt que de faire un nouveau temps pour chaque opération.

Et puis il y a l'élimination du DataContext avant que le IQueryable n'ait été évalué, mais c'est plus un avantage pour les gens qui ne comprennent pas IQueryable que le DataContext.

L'autre concept avec lequel je vois beaucoup de confusion est la syntaxe de requête vs la syntaxe d'expression. J'utiliserai celle qui est la plus simple à ce stade, en utilisant souvent la syntaxe d'expression. Beaucoup de gens ne réalisent toujours pas qu'ils produiront la même chose à la fin, Query est compilé dans Expression après tout.

Aaron Powell
la source
2
Avertissement: l'unité de travail peut être un petit programme avec le contexte de données comme singleton.
graffic
15
Vous ne devez pas utiliser le DataContext dans un singleton, ce n'est pas thread-safe.
Aaron Powell
3
@Slace, tous les programmes ne sont pas multithead, il est donc correct d'avoir le DataContext comme singleton dans beaucoup de logiciels "de bureau"
Ian Ringrose
2
J'ai été mordu par cela (en utilisant DataContext comme singleton) lorsque j'ai fait mon premier projet LINQ to SQL. Je ne pense pas que la documentation et les livres rendent cela assez évident. En fait, je pense que le nom pourrait être amélioré, mais je ne sais pas comment.
Roger Lipscombe
1
Il a fallu lire les artivles de ScottGu sur Linq plusieurs fois pour me faire pilonner la tête.
Evan Plaice
34

Je pense que la partie mal comprise de LINQ est qu'il s'agit d'une extension de langue , et non d'une extension ou d'une construction de base de données.

LINQest tellement plus que LINQ to SQL.

Maintenant que la plupart d'entre nous ont utilisé des LINQcollections, nous n'y retournerons JAMAIS!

LINQ est la fonctionnalité la plus importante de .NET depuis les génériques en 2.0 et les types anonymes en 3.0.

Et maintenant que nous avons Lambda, je ne peux pas attendre la programmation parallèle!

Chris
la source
Je l'appellerais même plus significatif que les types anonymes, et peut-être même plus que les génériques.
Justin Morgan
26

Pour ma part, j'aimerais savoir si j'ai besoin de savoir ce que sont les arbres d'expression et pourquoi.

Robert Rossney
la source
6
Je pense que cela vaut la peine de savoir ce que sont les arbres d'expression et pourquoi ils existent, mais pas les détails de la façon de les construire vous-même. (Ils sont difficiles à construire à la main, mais le compilateur fera un excellent travail lors de la conversion d'une expression lambda.)
Jon Skeet
3
En fait, je pensais à faire quelques entrées de blog sur les arbres d'expression (puisque je les "récupère"). Je trouve très utile de manipuler les arbres d'expression ...
Marc Gravell
Cependant, je ne pense pas qu'ils soient utiles pour le (s) discours de Jon ;-p
Marc Gravell
3
Je crains juste que les arbres d'expression ressemblent à la déclaration de rendement: quelque chose qui s'est avéré incroyablement précieux malgré le fait que je ne comprenais pas à quoi il servait au début.
Robert Rossney
1
Marc Gravell J'adorerais lire vos entrées de blog sur le sujet. Dans l'attente
Alexandre Brisebois
20

Je suis assez nouveau sur LINQ. Voici les choses sur lesquelles je suis tombé lors de ma première tentative

  • Combiner plusieurs requêtes en une seule
  • Débogage efficace des requêtes LINQ dans Visual Studio.
Mark Heath
la source
21
Le débogage de LINQ est un sujet à lui tout seul et important. Je pense que la plus grande faiblesse de LINQ est qu'il vous permet d'écrire des blocs de logique arbitrairement complexes que vous ne pouvez pas parcourir.
Robert Rossney
3
ceux-ci peuvent être un bon endroit pour utiliser le pad LINQ
Maslow
2
D'accord de bon cœur; c'est pourquoi j'ai écrit LINQ Secrets Revealed: Chaining and Debugging , juste publié sur Simple-Talk.com, que vous pourriez trouver utile.
Michael Sorens
Oui, LinqPad est un excellent outil secondaire pour développer vos requêtes LINQ. Surtout au début et vous êtes nouveau dans les conventions / modèles.
Buffalo
20

Quelque chose que je n'avais pas réalisé à l'origine, c'est que la syntaxe LINQ ne nécessite pasIEnumerable<T> ou ne fonctionne pasIQueryable<T> , LINQ concerne simplement la correspondance de modèles.

texte alternatif http://bartdesmet.info/images_wlw/QIsIQueryabletheRightChoiceforMe_13478/image_thumb_3.png

Voici la réponse (non, je n'ai pas écrit ce blog, Bart De Smet l'a fait, et c'est l'un des meilleurs blogueurs sur LINQ que j'ai trouvé).

Aaron Powell
la source
1
Vous pourriez également trouver cet article de blog intéressant: msmvps.com/blogs/jon_skeet/archive/2008/02/29/…
Jon Skeet
Nice post Jon (je ne m'abonne à votre blog que récemment).
Aaron Powell
19

J'ai toujours des problèmes avec la commande "let" (pour laquelle je n'ai jamais trouvé d'utilisation) et SelectMany (que j'ai utilisé, mais je ne suis pas sûr de l'avoir fait correctement)

James Curran
la source
2
Chaque fois que vous souhaitez introduire une variable, vous utilisez une instruction let. Pensez à une boucle traditionnelle dans laquelle vous introduisez des variables et donnez à chaque variable un nom pour aider à la lisibilité du code. Parfois, il est également agréable d'avoir une instruction let évaluant le résultat d'une fonction, que vous pouvez ensuite sélectionner et classer par sans avoir à évaluer le résultat deux fois.
Rob Packwood
'let' vous permet de faire des types composites. Des trucs pratiques.
Phill
19

Comprendre quand l'abstraction entre les fournisseurs Linq fuit. Certaines choses fonctionnent sur des objets mais pas sur SQL (par exemple, .TakeWhile). Certaines méthodes peuvent être traduites en SQL (ToUpper) tandis que d'autres ne le peuvent pas. Certaines techniques sont plus efficaces dans les objets où d'autres sont plus efficaces dans SQL (différentes méthodes de jointure).

denis phillips
la source
1
C'est un très bon point. Cela n'aide pas qu'Intellisense vous les montre TOUS et il sera même généralement compilé. Ensuite, vous explosez à l'exécution. J'espère que VS 2010 montre mieux les méthodes d'extension pertinentes.
Jason Short
12

Quelques choses.

  1. Les gens pensent que Linq est Linq to SQL.
  2. Certaines personnes pensent qu'elles peuvent commencer à remplacer toutes les foreach / logic par des requêtes Linq sans tenir compte de ces implications en termes de performances.
Krishna Kumar
la source
11

OK, en raison de la demande, j'ai écrit quelques trucs sur l'expression. Je ne suis pas 100% satisfait de la façon dont le blogueur et LiveWriter ont conspiré pour le formater, mais cela suffira pour l'instant ...

Quoi qu'il en soit, voici ... J'adorerais tout commentaire, surtout s'il y a des domaines où les gens veulent plus d'informations.

Ça y est , on aime ou on déteste ...

Marc Gravell
la source
10

Certains des messages d'erreur, en particulier de LINQ à SQL, peuvent être assez déroutants. sourire

J'ai été mordu par l'exécution différée à quelques reprises comme tout le monde. Je pense que la chose la plus déroutante pour moi a été le fournisseur de requêtes SQL Server et ce que vous pouvez et ne pouvez pas faire avec.

Je suis toujours étonné par le fait que vous ne pouvez pas faire un Sum () sur une colonne décimale / monétaire qui est parfois vide. L'utilisation de DefaultIfEmpty () ne fonctionnera tout simplement pas. :(

Per Erik Stendahl
la source
1
Shold be prette facile to slap a Where on that query to make sum work
Esben Skov Pedersen
9

Je pense qu'une grande chose à couvrir dans LINQ est de savoir comment vous pouvez avoir des ennuis en termes de performances. Par exemple, utiliser le décompte de LINQ comme condition de boucle n'est vraiment, vraiment pas intelligent.

Steve
la source
7

Que IQueryable accepte les deux, Expression<Func<T1, T2, T3, ...>>et Func<T1, T2, T3, ...>sans donner un indice sur la dégradation des performances dans le deuxième cas.

Voici un exemple de code, qui montre ce que je veux dire:

[TestMethod]
public void QueryComplexityTest()
{
    var users = _dataContext.Users;

    Func<User, bool>                funcSelector =       q => q.UserName.StartsWith("Test");
    Expression<Func<User, bool>>    expressionSelector = q => q.UserName.StartsWith("Test");

    // Returns IEnumerable, and do filtering of data on client-side
    IQueryable<User> func = users.Where(funcSelector).AsQueryable();
    // Returns IQuerible and do filtering of data on server side
    // SELECT ... FROM [dbo].[User] AS [t0] WHERE [t0].[user_name] LIKE @p0
    IQueryable<User> exp = users.Where(expressionSelector);
}
Valera Kolupaev
la source
Peux-tu expliquer? Je ne suis pas en
train de
@Pretzel J'ai ajouté un exemple de code qui illustre mon problème.
Valera Kolupaev,
Merci pour l'exemple de code! Très utile.
Buffalo
6

Je ne sais pas si cela peut être mal compris - mais pour moi, tout simplement inconnu.

J'ai été ravi d'en savoir plus sur DataLoadOptions et sur la façon dont je peux contrôler les tables jointes lorsque je fais une requête particulière.

Voir ici pour plus d'informations: MSDN: DataLoadOptions

Martin
la source
6

Je dirais que l'aspect le plus mal compris (ou cela ne devrait-il pas être compris?) De LINQ est IQueryable et les fournisseurs LINQ personnalisés .

J'utilise LINQ depuis un certain temps maintenant et je suis parfaitement à l'aise dans le monde IEnumerable et je peux résoudre la plupart des problèmes avec LINQ.

Mais quand j'ai commencé à regarder et à lire sur IQueryable, et les expressions et les fournisseurs de linq personnalisés, cela m'a fait tourner la tête. Jetez un œil au fonctionnement de LINQ to SQL si vous voulez voir une logique assez complexe.

J'ai hâte de comprendre cet aspect de LINQ ...

Jack Ukleja
la source
6

Comme la plupart des gens l'ont dit, je pense que la partie la plus mal comprise est de supposer que LINQ est juste un remplacement pour T-SQL. Mon manager qui se considère comme un gourou de TSQL ne nous laisserait pas utiliser LINQ dans notre projet et déteste même MS pour avoir publié une telle chose !!!

HashName
la source
Trop de gens l'utilisent en remplacement de TSQL. La plupart d'entre eux n'ont jamais entendu parler d'un plan d'exécution.
erikkallen
+1 parce que je suis d'accord avec votre responsable, au moins dans la mesure où vous autorisez LINQ to SQL dans n'importe quel projet. LINQ to Objects est une tout autre affaire.
NotMe
5

Que représente var lorsqu'une requête est exécutée?

Est - il iQueryable, iSingleResult, iMultipleResultou le fait changer en fonction du la mise en œuvre. Il y a des spéculations sur l'utilisation (ce qui semble être) du typage dynamique par rapport au typage statique standard en C #.

31939
la source
AFAIK var est toujours la classe concrète en question (même si c'est un type anonyme) donc ce n'est jamais IQueryable, ISingleResult ou quoi que ce soit commençant par 'I' (les classes concrètes qui commencent par 'I' n'ont pas besoin de s'appliquer).
Motti
5

Je pense que tout le monde ne comprend pas à quel point il est facile d'imbriquer une boucle.

Par exemple:

from outerloopitem in outerloopitems
from innerloopitem in outerloopitem.childitems
select outerloopitem, innerloopitem
Rob Packwood
la source
+1, whoa. c'est assez puissant.
Pretzel
4

group by me fait encore tourner la tête.

Toute confusion concernant une exécution différée devrait pouvoir être résolue en parcourant un code simple basé sur LINQ et en jouant dans la fenêtre de surveillance.

Richard Ev
la source
1
J'ai trouvé qu'implémenter un peu de LINQ to Objects pour le plaisir aide vraiment :) Mais oui, c'est un peu déroutant - certainement si je n'ai pas fait de LINQ pendant un certain temps, je dois revenir aux signatures. De même, «rejoindre» vs «rejoindre» me fait souvent ...
Jon Skeet
4

Requêtes compilées

Le fait que vous ne puissiez pas chaîner IQueryablecar ce sont des appels de méthode (alors qu'il n'y a rien d'autre que SQL traduisible!) Et qu'il est presque impossible de contourner cela est ahurissant et crée une énorme violation de DRY. J'ai besoin de mon IQueryablepour ad-hoc dans lequel je n'ai pas de requêtes compilées (je n'ai que des requêtes compilées pour les scénarios lourds), mais dans les requêtes compilées, je ne peux pas les utiliser et à la place, j'ai besoin d'écrire à nouveau la syntaxe de requête régulière. Maintenant, je fais les mêmes sous-requêtes à 2 endroits, je dois me rappeler de mettre à jour les deux si quelque chose change, etc. Un cauchemar.

Alex
la source
4

Je pense que l'idée fausse n ° 1 sur LINQ to SQL est que vous DEVEZ TOUJOURS CONNAÎTRE SQL afin de l'utiliser efficacement.

Une autre chose mal comprise à propos de Linq to Sql est que vous devez encore abaisser la sécurité de votre base de données au point d'absurdité pour le faire fonctionner.

Un troisième point est que l'utilisation de Linq à Sql avec des classes dynamiques (ce qui signifie que la définition de classe est créée au moment de l'exécution) provoque une énorme quantité de compilation juste à temps. Ce qui peut absolument tuer les performances.

Pas moi
la source
4
Il est cependant très avantageux de déjà connaître SQL. Certains SQL émis par Linq vers SQL (et d'autres ORM) peuvent être carrément douteux, et la connaissance de SQL aide à diagnostiquer de tels problèmes. De plus, Linq to SQL peut utiliser des procédures stockées.
Robert Harvey
2

Comme mentionné, chargement paresseux et exécution différée

En quoi LINQ to Objects et LINQ to XML (IEnumerable) sont différents de LINQ to SQL (IQueryable)

COMMENT créer une couche d'accès aux données, une couche métier et une couche de présentation avec LINQ dans toutes les couches ... et un bon exemple.

Machine à cendres
la source
Les deux premiers que je peux faire. Je ne voudrais pas encore essayer de faire le troisième dans le sens "c'est la bonne façon de le faire" ...
Jon Skeet
+1, jusqu'à ce que vous le signaliez, je n'avais pas réalisé que LINQ-to-Objects et LINQ-to-XML étaient IEnumerable par opposition à LINQ-to-SQL comme IQueryable, mais cela a du sens. Merci!
Pretzel
2

Comme la plupart des gens l'ont dit, je pense que la partie la plus mal comprise est de supposer que LINQ est juste un remplacement pour T-SQL. Mon manager qui se considère comme un gourou de TSQL ne nous laisserait pas utiliser LINQ dans notre projet et déteste même MS pour avoir publié une telle chose !!!

stackuser1
la source
2

Transactions (sans utiliser TransactionScope)

Naeem Sarfraz
la source