Le mot-clé let de linq est-il meilleur que son mot-clé into?

86

Je suis actuellement en train de réviser LINQ et j'essaie de comprendre la différence entre le letet l'utilisation du intomot - clé. Jusqu'à présent, le letmot - clé semble meilleur que le intomot - clé dans la mesure où je comprends.

Le intomot-clé permet essentiellement de poursuivre une requête après une projection. (Je veux juste indiquer explicitement que je ne fais pas référence à celui de la jointure de groupe.)

Étant donné un tableau de noms, cela permet de faire ce qui suit:

var intoQuery =
  from n in names
  select Regex.Replace(n, "[aeiou]", "")
  into noVowel
  where noVowel.Length > 2
  select noVowel;

Il prend le résultat de la sélection et la place dans la noVowelvariable qui permet alors d'introduire d' autres where, orderbyet les selectclauses. Une fois la noVowelvariable créée, la nvariable n'est plus disponible.

Le letmot-clé, quant à lui, utilise des types anonymes temporaires pour vous permettre de réutiliser plusieurs variables à la fois.

Vous pouvez faire ce qui suit:

var letQuery =
  from n in names
  let noVowel = Regex.Replace(n, "[aeiou]", "")
  where noVowel.Length > 2
  select noVowel;

Les variables noVowelet nsont disponibles pour utilisation (même si je ne les ai pas utilisées dans ce cas).

Bien que je puisse voir la différence, je ne peux pas vraiment comprendre pourquoi on voudrait utiliser le intomot - clé sur le letmot - clé à moins que l'on ne veuille explicitement s'assurer que les variables précédentes ne pouvaient pas être utilisées dans les dernières parties de la requête.

Alors, y a-t-il une bonne raison pour laquelle les deux mots-clés existent?

mezoid
la source
Est-ce une faute de frappe dans l' letexemple - where noVowel, qu'est-ce que c'est noVoweldans ce cas?
sll

Réponses:

85

Oui, parce qu'ils font des choses différentes, comme vous l'avez dit.

select ... intoisole efficacement l'ensemble d'une requête et vous permet de l'utiliser comme entrée d'une nouvelle requête. Personnellement, je préfère généralement le faire via deux variables:

var tmp = from n in names
          select Regex.Replace(n, "[aeiou]", "");

var noVowels = from noVowel in tmp
               where noVowel.Length > 2
               select noVowel;

(Certes, dans ce cas, je le ferais avec une notation par points sur deux lignes, mais en ignorant cela ...)

Souvent, vous ne voulez pas tout le bagage de la partie précédente de la requête - c'est-à-dire lorsque vous utilisez select ... intoou divisez la requête en deux comme dans l'exemple ci-dessus. Non seulement cela signifie que les parties antérieures de la requête ne peuvent pas être utilisées alors qu'elles ne devraient pas l'être, mais cela simplifie ce qui se passe - et bien sûr, cela signifie qu'il y a potentiellement moins de copies à chaque étape.

D'autre part, lorsque vous ne voulez garder le reste du contexte, letest plus logique.

Jon Skeet
la source
9
L'utilisation de l'un ou de l'autre affecte-t-elle le SQL généré?
Pat Niemeyer
44

La principale différence est l' letinjection de la variable dans le contexte / la portée, où intocrée un nouveau contexte / portée.

leppie
la source
1

Voulant connaître la différence côté DB, a écrit 2 requêtes Entity Framework.

  • Laisser

    from u in Users
    let noVowel = u.FirstName.Replace("a","").Replace("e","").Replace("i","")
    where noVowel.Length >5
    select new {u.FirstName, noVowel}
    
  • Dans

    from u in Users
    select u.FirstName.Replace("a","").Replace("e","").Replace("i","")
    into noVowel
    where noVowel.Length >5
    select noVowel
    

Les SQL générés sont presque identiques . Le SQL n'est pas parfait, le même code de processus de chaîne est répété à 2 endroits (où et sélectionnez).

SELECT 1 AS [C1], [Extent1].[FirstName] AS [FirstName], 
REPLACE(REPLACE(REPLACE([Extent1].[FirstName], N'a', N''), N'e', N''), N'i', N'') AS [C2]
FROM [dbo].[User] AS [Extent1]
WHERE ( CAST(LEN(REPLACE(REPLACE(REPLACE([Extent1].[FirstName], N'a', N''), N'e', N''), N'i', N'')) AS int)) > 5
GO

SELECT 
REPLACE(REPLACE(REPLACE([Extent1].[FirstName], N'a', N''), N'e', N''), N'i', N'') AS [C1]
FROM [dbo].[User] AS [Extent1]
WHERE ( CAST(LEN(REPLACE(REPLACE(REPLACE([Extent1].[FirstName], N'a', N''), N'e', N''), N'i', N'')) AS int)) > 5

Voici le SQL généré par LINQ-to-SQL

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'a'
DECLARE @p1 NVarChar(1000) = ''
DECLARE @p2 NVarChar(1000) = 'e'
DECLARE @p3 NVarChar(1000) = ''
DECLARE @p4 NVarChar(1000) = 'i'
DECLARE @p5 NVarChar(1000) = ''
DECLARE @p6 Int = 5
-- EndRegion
SELECT [t1].[FirstName], [t1].[value] AS [noVowel]
FROM (
    SELECT [t0].[FirstName], REPLACE(REPLACE(REPLACE([t0].[FirstName], @p0, @p1), @p2, @p3), @p4, @p5) AS [value]
    FROM [User] AS [t0]
    ) AS [t1]
WHERE LEN([t1].[value]) > @p6
GO

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'a'
DECLARE @p1 NVarChar(1000) = ''
DECLARE @p2 NVarChar(1000) = 'e'
DECLARE @p3 NVarChar(1000) = ''
DECLARE @p4 NVarChar(1000) = 'i'
DECLARE @p5 NVarChar(1000) = ''
DECLARE @p6 Int = 5
-- EndRegion
SELECT [t1].[value]
FROM (
    SELECT REPLACE(REPLACE(REPLACE([t0].[FirstName], @p0, @p1), @p2, @p3), @p4, @p5) AS [value]
    FROM [User] AS [t0]
    ) AS [t1]
WHERE LEN([t1].[value]) > @p6

Semble Linq-to-SQL est plus intelligent que Entity Framework, le processus de chaîne effectué une seule fois.

Rm558
la source
0

Version visualisée de la réponse de Leppie . Comme on peut le voir, le compilateur génère une erreur dans la requête avec, intocontrairement à la dernière, l'accès à la première variable.

entrez la description de l'image ici

snr
la source