Contexte: Au cours du mois prochain, je donnerai trois conférences sur ou au moins incluant LINQ
dans le contexte de C#
. J'aimerais savoir quels sujets méritent de retenir beaucoup d'attention, en fonction de ce que les gens peuvent trouver difficile à comprendre ou de ce qu'ils peuvent avoir une fausse impression. Je ne parlerai pas spécifiquement de LINQ
à SQL
ou d'Entity Framework, sauf à titre d'exemple de la manière 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é dur à propos de LINQ
? Qu'avez-vous vu en termes de malentendus? Les exemples peuvent être les suivants, mais ne vous limitez pas vous-même!
C#
le compilateur traite les expressions de requêteIQueryable
Exécution différée
Je sais que le concept d’exécution différée devrait maintenant être intégré à moi, mais cet exemple m’a vraiment aidé à en saisir concrètement le sens:
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 ce qui suit:
Before add:
Bob
Alice
Trent
After add:
Bob
Alice
Trent
Mallory
Qu'il y a plus que LINQ
à SQL
et que les fonctionnalités ne sont pas seulement un analyseur SQL
incorporé dans le langage.
notation Big O . LINQ facilite énormément l'écriture d'algorithmes O (n ^ 4) sans s'en rendre compte, si vous ne savez pas ce que vous faites.
Je pense que le fait qu'une expression Lambda
puisse être résolue à la fois par une arborescence d'expression et par un délégué anonyme, vous pouvez donc transmettre la même expression déclarative lambda
à la fois IEnumerable<T>
méthodes d'extension et IQueryable<T>
méthodes d'extension.
Cela m'a pris chemin trop longtemps pour comprendre que de nombreuses méthodes d'extension LINQ telles que Single()
, SingleOrDefault()
etc. ont des surcharges qui prennent lambdas.
Tu peux faire :
Single(x => x.id == id)
et je n'ai pas besoin de le dire - ce que certains mauvais tutoriels m'ont fait prendre l'habitude de faire
Where(x => x.id == id).Single()
Dans LINQ to SQL, je vois constamment des personnes ne comprenant pas le DataContext, comment il peut être utilisé et comment il devrait être utilisé. Trop de personnes ne voient pas le DataContext pour ce qu'il est, un objet Unit of Work, pas un objet persistant.
J'ai souvent vu des personnes essayer de singleton un DataContext/session it/etc plutôt que de créer une nouvelle heure pour chaque opération.
Et puis, il y a l'élimination du DataContext avant l'évaluation d'IQueryable, mais c'est plus un problème pour les personnes qui ne comprennent pas IQueryable que le DataContext.
L'autre concept avec lequel je vois beaucoup de confusion est la syntaxe requête/syntaxe d'expression. Je vais utiliser celui qui est le plus facile à ce stade, en m'appuyant souvent sur 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.
Je pense que la la partie mal comprise de LINQ est que c'est un extension de langue, pas une extension de base de données ni une construction.
LINQ
est tellement plus que LINQ to SQL
.
Maintenant que la plupart d'entre nous ont utilisé LINQ
sur des collections, nous n'y retournerons JAMAIS!
LINQ
est la fonctionnalité la plus importante pour .NET depuis Generics dans la version 2.0 et les types anonymes dans la version 3.0.
Et maintenant que nous avons Lambda, je ne peux pas attendre pour la programmation parallèle!
Pour ma part, j'aimerais bien savoir si j'ai besoin de savoir ce que sont les arbres d'expression, et pourquoi.
Je suis assez nouveau pour LINQ. Voici les choses que j'ai trébuché dans ma première tentative
Quelque chose que je n'avais pas compris à l'origine était que la syntaxe LINQ non nécessite IEnumerable<T>
ou IQueryable<T>
pour fonctionner, LINQ n’est qu’une correspondance de motif.
alt text 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 il est l'un des meilleurs blogueurs sur LINQ que j'ai trouvés).
J'ai toujours des problèmes avec les commandes "let" (pour lesquelles je n'ai jamais trouvé d'utilisation) et SelectMany (que j'ai utilisées, mais je ne suis pas sûr de l'avoir bien fait)
Comprendre quand l'abstraction chez 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), d’autres pas. Certaines techniques sont plus efficaces dans les objets où d'autres sont plus efficaces en SQL (méthodes de jointure différentes).
Un couple de choses.
OK, à cause de la demande, j'ai rédigé certains des éléments d'Expression. Je ne suis pas entièrement satisfait de la façon dont Blogger et LiveWriter ont conspiré pour le formater, mais ça ira pour le moment ...
Quoi qu'il en soit, voici ... Je serais ravi de recevoir vos commentaires, surtout s'il y a des endroits où les gens veulent plus d'informations.
Le voici , aime ou déteste ça ...
Certains messages d'erreur, en particulier de LINQ à SQL, peuvent être assez déroutants. sourire
L'exécution différée m'a mordue à plusieurs 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 de Sum () sur une colonne décimale/argent qui est parfois vide. L'utilisation de DefaultIfEmpty () ne fonctionnera tout simplement pas. :(
Je pense qu’une bonne chose à couvrir dans LINQ est de savoir comment vous pouvez vous mettre en difficulté sur le plan des performances. Par exemple, utiliser le nombre de LINQ comme condition de boucle n'est vraiment pas très intelligent.
Que IQueryable accepte les deux, Expression<Func<T1, T2, T3, ...>>
et Func<T1, T2, T3, ...>
, sans donner d'indication sur la dégradation des performances dans le 2e cas.
Voici un exemple de code montrant 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);
}
Je dirais que l'aspect le plus mal compris (ou devrait-il être incompris?) De LINQ est IQueryable et fournisseurs LINQ personnalisés.
J'utilise LINQ depuis un moment et je suis complètement à l'aise dans le monde IEnumerable et je peux résoudre la plupart des problèmes avec LINQ.
Mais lorsque j'ai commencé à examiner IQueryable, ainsi qu'à Expressions et aux fournisseurs linq personnalisés, j'ai eu le vertige. Examinez le fonctionnement de LINQ to SQL si vous voulez voir une logique assez complexe.
J'ai hâte de comprendre cet aspect de LINQ ...
Je ne sais pas si cela peut être qualifié de mal compris - mais pour moi, tout simplement inconnu.
J'ai été ravi d'apprendre à propos de DataLoadOptions et à savoir comment contrôler les tables jointes lors de l'exécution d'une requête.
Voir ici pour plus d’informations: MSDN: DataLoadOptions
Comme la plupart des gens l'ont dit, je pense que la partie la plus mal comprise consiste à supposer que LINQ ne fait que remplacer T-SQL. Mon manager qui se considère en tant que gourou de TSQL ne nous laisserait pas utiliser LINQ dans notre projet et déteste même MS pour avoir publié une telle chose !!!
Je ne pense pas que tout le monde comprenne à quel point il est facile d'imbriquer une boucle.
Par exemple:
from outerloopitem in outerloopitems
from innerloopitem in outerloopitem.childitems
select outerloopitem, innerloopitem
Que représente var quand une requête est exécutée?
Est-ce iQueryable
, iSingleResult
, iMultipleResult
ou change-t-il en fonction de l'implémentation? Il y a des spéculations sur l'utilisation (ce qui semble être) du typage dynamique par rapport au typage statique standard en C #.
group by
me fait encore tourner la tête.
Toute confusion à propos de exécution différée devrait pouvoir être résolue en parcourant un simple code basé sur LINQ et en jouant dans la fenêtre de surveillance.
Le fait que vous ne puissiez pas chaîner IQueryable
car il s'agit d'appels de méthode (while toujours rien d'autre que du SQL traduisible!) Et qu'il est presque impossible de contourner ce problème est ahurissant et crée une énorme violation de DRY. J'ai besoin de mon IQueryable
pour les tâches ad-hoc dans lesquelles 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, mais je dois écrire régulièrement requête à nouveau la syntaxe. Maintenant, je fais les mêmes sous-requêtes à 2 endroits, vous devez vous rappeler de mettre à jour les deux si quelque chose change, et ainsi de suite. Un cauchemar.
Je pense que la première idée fausse à propos de 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 toujours réduire la sécurité de votre base de données à un point d’absurdité pour que cela fonctionne.
Un troisième point est que l'utilisation de Linq to Sql avec les classes dynamiques (ce qui signifie que la définition de classe est créée à l'exécution) provoque une énorme quantité de compilation juste à temps. Ce qui peut tuer absolument les performances.
Chargement paresseux.
Transactions (sans utiliser TransactionScope)
Comme la plupart des gens l'ont dit, je pense que la partie la plus mal comprise consiste à supposer que LINQ ne fait que remplacer T-SQL. Mon manager qui se considère comme un gourou de TSQL ne nous laissera pas utiliser LINQ dans notre projet et déteste même MS pour avoir publié une telle chose !!!
Comme mentionné, chargement paresseux et exécution différée
Comment LINQ to Objects et LINQ to XML (IEnumerable) sont différents de LINQ to SQL (IQueryable)
COMMENT pour construire une couche d'accès aux données, une couche métier et une couche présentation avec LINQ dans toutes les couches ... et un bon exemple.
Syntaxe de compréhension 'magique'. Comment la syntaxe de compréhension est-elle traduite en appels de méthodes et quels appels de méthodes sont choisis.
Comment, par exemple:
from a in b
from c in d
where a > c
select new { a, c }
se traduit en appels de méthode.
J'ai eu du mal à trouver des informations claires sur les types anonymes, notamment en ce qui concerne les performances dans les applications Web. De plus, je proposerais des exemples d’expressions Lamda de meilleure qualité et plus pratiques et une section "Comment faire" dans les rubriques relatives aux interrogations et aux performances.
J'espère que ma brève liste pourra vous aider!
Expliquez pourquoi Linq ne gère pas la jointure externe gauche aussi simplement que dans la syntaxe SQL. Voir ces articles: Implémentation d'une jointure gauche avec LINQ , Comment: effectuer des jointures externes gauches (Guide de programmation C #) Je suis tellement déçu quand je suis tombé sur cet obstacle le respect de la langue a disparu et j'ai décidé que c'était simplement quelque chose qui disparaîtrait rapidement. Aucune personne sérieuse ne voudrait travailler avec une syntaxe qui manque de ces primitives éprouvées sur le champ de bataille. Si vous pouviez expliquer pourquoi ces opérations ne sont pas prises en charge. Je deviendrais une personne meilleure et plus ouverte d'esprit.
Je pense que vous devriez accorder plus d’attention aux fonctionnalités les plus couramment utilisées de LINQ - les expressions lambda et les types anonymes, plutôt que de perdre du temps sur des éléments "difficiles à comprendre" qui sont rarement utilisés dans les programmes du monde réel.
Ce n'est bien sûr pas "le plus difficile" mais juste quelque chose à ajouter à la liste:
ThenBy() extension method
Sans regarder sa mise en œuvre, je suis d'abord perplexe quant à son fonctionnement. Tout le monde comprend très bien comment les champs de tri séparés par des virgules fonctionnent en SQL - mais à première vue, je suis sceptique sur le fait que ThenBy fasse ce que je veux vraiment. Comment peut-il "savoir" ce qu'était le champ de tri précédent - il semble que cela devrait l'être.
Je pars à la recherche maintenant ...
Je trouve que "Créer un arbre d'expression" est difficile. Il y a beaucoup de choses qui me dérangent avec ce que vous pouvez faire avec LINQ, LINQ to SQL et ADO.Net.
Pour LINQ2SQL: Comprenez une partie du code SQL généré et écrivez des requêtes LINQ qui se traduisent par un code SQL correct (rapide). Cela fait partie de la problématique plus générale de savoir comment équilibrer la nature déclarative des requêtes LINQ avec le réalisme dont elles ont besoin pour s'exécuter rapidement dans un environnement connu (SQL Server).
Vous pouvez obtenir une requête générée par SQL complètement différente en modifiant une toute petite chose dans le code LINQ. Peut être particulièrement dangereux si vous créez un arbre d’expression basé sur des instructions conditionnelles (c’est-à-dire en ajoutant des critères de filtrage facultatifs).
Supposons que nous ayons une table avec 3 champs. A, B et C (Ce sont des entiers et le nom de la table est "Table1").
Je le montre comme ceci:
[A, B, C]
Maintenant, nous voulons obtenir un résultat tel que celui-ci:
[X = A, Y = B + C]
Et nous avons un tel cours:
public class Temp
{
public Temp(int x, int y)
{
this.X = x;
this.Y = y;
}
public int X { get; private set; }
public int Y { get; private set; }
}
Ensuite, nous l'utilisons comme ceci:
using (MyDataContext db = new MyDataContext())
{
var result = db.Table1.Select(row =>
new Temp(row.A, row.B + row.C)).ToList();
}
La requête SQL générée est:
SELECT [t0].[A] AS [x], [t0].[B] + [t0].[C] AS [y]
FROM [Table1] AS [t0]
Il traduit le .ctor du temp. Il sait que je veux que "row.B + row.C" (encore plus ...) mette le paramètre "y" de mon constructeur de classe!
Ces traductions sont très intéressantes pour moi. J'aime ça et je pense qu'écrire de tels traducteurs (LINQ to Something) est un peu difficile!
Bien sûr! C'est une mauvaise nouvelle: LINQ to Entities (4.0) ne prend pas en charge les constructeurs avec paramètres. (Pourquoi pas?)
Qui est plus rapide, en ligne Linq-to-Sql ou Linq-to-Sql en utilisant Tsql Sprocs
... et existe-t-il des cas où il est préférable d'utiliser des requêtes côté serveur (Sproc) ou côté client (Linq en ligne).
Je trouve un peu décevant que la syntaxe d'expression de requête ne prenne en charge qu'un sous-ensemble de la fonctionnalité LINQ. Vous ne pouvez donc pas éviter de chaîner les méthodes d'extension de temps en temps. Par exemple. la méthode Distinct
ne peut pas être appelée à l'aide de la syntaxe d'expression de requête. Pour utiliser la méthode Distinct
, vous devez appeler la méthode d'extension. D'autre part, la syntaxe de l'expression de requête est très pratique dans de nombreux cas, vous ne devez donc pas l'ignorer.
Une conférence sur LINQ pourrait inclure des instructions pratiques pour savoir quand préférer une syntaxe à une autre et comment les mélanger.
Le fait que vous ne puissiez pas chaîner IQueryable parce que ce sont des appels de méthode (même si rien n'est autre que du SQL traduisible!) Et qu'il est presque impossible de le contourner est ahurissant et crée une énorme violation de DRY. J'ai besoin de mon IQueryable pour les tâches ad-hoc dans lesquelles 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 je dois à nouveau écrire la syntaxe de requête régulière. Maintenant, je fais les mêmes sous-requêtes à 2 endroits, vous devez vous rappeler de mettre à jour les deux si quelque chose change, et ainsi de suite. Un cauchemar.