Ist das Schlüsselwort let von linq besser als das Schlüsselwort into in?

86

Ich aktualisiere gerade LINQ und versuche, den Unterschied zwischen dem letund dem intoSchlüsselwort zu verstehen . Bisher letscheint das intoSchlüsselwort meines Wissens besser zu sein als das Schlüsselwort.

Das intoSchlüsselwort ermöglicht es im Wesentlichen, eine Abfrage nach einer Projektion fortzusetzen. (Ich möchte nur ausdrücklich darauf hinweisen, dass ich mich nicht auf die für den Gruppenbeitritt beziehe.)

Bei einer Reihe von Namen kann man Folgendes tun:

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

Es nimmt das Ergebnis der Auswahl und legt sie in die noVowelVariable , die dann eine weitere einführen können where, orderbyund selectKlauseln. Sobald die noVowelVariable erstellt wurde, nist sie nicht mehr verfügbar.

Das letSchlüsselwort verwendet dagegen temporäre anonyme Typen, damit Sie mehr als eine Variable gleichzeitig wiederverwenden können.

Sie können Folgendes tun:

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

Sowohl die noVowelals auch die nVariablen stehen zur Verwendung zur Verfügung (obwohl ich sie in diesem Fall nicht verwendet habe).

Obwohl ich den Unterschied sehen kann, kann ich nicht ganz verstehen, warum man das intoSchlüsselwort über dem letSchlüsselwort verwenden möchte, es sei denn, man wollte ausdrücklich sicherstellen, dass vorherige Variablen in späteren Teilen der Abfrage nicht verwendet werden konnten.

Gibt es einen guten Grund, warum beide Schlüsselwörter existieren?

Mezoid
quelle
Ist es ein Tippfehler im letBeispiel - where noVowelwas ist noVowelin diesem Fall?
sll

Antworten:

85

Ja, weil sie verschiedene Dinge tun, wie Sie gesagt haben.

select ... intoIsoliert effektiv die gesamte Abfrage und lässt Sie sie als Eingabe für eine neue Abfrage verwenden. Persönlich bevorzuge ich dies normalerweise über zwei Variablen:

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

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

(Zugegeben, in diesem Fall würde ich es mit Punktnotation in zwei Zeilen machen, aber das ignorieren ...)

Oft möchten Sie nicht das gesamte Gepäck des früheren Teils der Abfrage - wenn Sie select ... intodie Abfrage gemäß dem obigen Beispiel verwenden oder in zwei Teile teilen. Dies bedeutet nicht nur, dass die früheren Teile der Abfrage nicht verwendet werden können, wenn sie nicht verwendet werden sollten, sondern vereinfacht auch die Vorgänge - und bedeutet natürlich, dass bei jedem Schritt möglicherweise weniger kopiert wird.

Auf der anderen Seite ist es sinnvoller , wenn Sie den Rest des Kontexts beibehalten möchten let.

Jon Skeet
quelle
9
Beeinflusst die Verwendung des einen oder anderen das generierte SQL?
Pat Niemeyer
44

Der Hauptunterschied besteht darin, dass die letVariable in den Kontext / Bereich eingefügt wird, wo intoein neuer Kontext / Bereich erstellt wird.

Leppie
quelle
1

Um den Unterschied auf der DB-Seite zu erfahren, wurden 2 Entity Framework-Abfragen geschrieben.

  • Lassen

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

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

Die generierten SQLs sind nahezu identisch . Die SQL ist nicht perfekt, der gleiche String-Prozesscode wird an zwei Stellen wiederholt (wo und auswählen).

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

Hier ist das von LINQ-to-SQL generierte 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

Linq-to-SQL ist anscheinend intelligenter als Entity Framework. Der Zeichenfolgenprozess wird nur einmal ausgeführt.

Rm558
quelle
0

Visualisierte Version von Leppies Antwort . Wie zu sehen ist, gibt der Compiler einen Fehler in der Abfrage aus, wobei im intoGegensatz zu letzterem der Zugriff auf die erste Variable erfolgt.

Geben Sie hier die Bildbeschreibung ein

snr
quelle