Czy linq's let słowo kluczowe jest lepsze niż jego słowo kluczowe w?

86

Obecnie odświerzam LINQ i próbuję zrozumieć różnicę między leti używając intosłowa kluczowego. Jak dotąd letsłowo kluczowe wydaje się lepsze niż intosłowo kluczowe, o ile wiem.

Słowo intokluczowe zasadniczo umożliwia kontynuowanie zapytania po projekcji. (Chcę tylko wyraźnie powiedzieć, że nie odnoszę się do tego, który służy do dołączania do grupy).

Biorąc pod uwagę tablicę nazw, można wykonać następujące czynności:

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

Zajmuje wynik select i umieszcza ją w noVowelzmiennej, która następnie pozwala na wprowadzenie dodatkowych where, orderbyoraz selectklauzule. Po noVowelutworzeniu nzmiennej nie jest już dostępna.

Z letdrugiej strony słowo kluczowe używa tymczasowych typów anonimowych, aby umożliwić ponowne użycie więcej niż jednej zmiennej naraz.

Możesz wykonać następujące czynności:

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

Dostępne są zarówno zmienne, jak noVoweli n(mimo że nie używałem ich w tym przypadku).

Chociaż widzę różnicę, nie bardzo rozumiem, dlaczego ktoś miałby chcieć użyć intosłowa kluczowego zamiast słowa letkluczowego, chyba że wyraźnie chciałoby się upewnić, że poprzednie zmienne nie mogą być używane w późniejszych częściach zapytania.

Czy jest więc dobry powód, dla którego istnieją oba słowa kluczowe?

mezoid
źródło
Czy to na letprzykład literówka - where noVowelco noVowelw takim razie jest?
SLL

Odpowiedzi:

85

Tak, ponieważ robią różne rzeczy, jak powiedziałeś.

select ... intoskutecznie izoluje całość jednego zapytania i umożliwia użycie go jako danych wejściowych do nowego zapytania. Osobiście zazwyczaj wolę to robić za pomocą dwóch zmiennych:

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

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

(Co prawda w tym przypadku zrobiłbym to z notacją kropkową w dwóch wierszach, ale ignorując to ...)

Często nie chcesz całego bagażu z wcześniejszej części zapytania - czyli gdy używasz select ... intolub dzielisz zapytanie na dwie części, jak w powyższym przykładzie. Nie tylko oznacza to, że wcześniejsze części zapytania nie mogą być używane, gdy nie powinny, ale także upraszcza to, co się dzieje - i oczywiście oznacza, że ​​na każdym kroku odbywa się potencjalnie mniej kopiowania.

Z drugiej strony, jeśli nie chcesz zachować resztę kontekście letwiększy sens.

Jon Skeet
źródło
9
Czy użycie jednej lub drugiej wpływa na wygenerowany kod SQL?
Pat Niemeyer
44

Podstawową różnicą jest letwstrzyknięcie zmiennej do kontekstu / zakresu, gdzie intotworzy nowy kontekst / zakres.

leppie
źródło
1

Chcąc poznać różnicę po stronie bazy danych, napisał 2 zapytania Entity Framework.

  • Pozwolić

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

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

Wygenerowane SQL są prawie identyczne . SQL nie jest doskonały, ten sam łańcuchowy kod procesu jest powtarzany w 2 miejscach (gdzie i wybierz).

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

Oto kod SQL wygenerowany przez 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

Wydaje się, że Linq-to-SQL jest inteligentniejsze niż Entity Framework, proces tworzenia ciągu znaków jest wykonywany tylko raz.

Rm558
źródło
0

Wizualizowana wersja odpowiedzi leppiego . Jak widać, kompilator zwraca błąd w zapytaniu, w intoodróżnieniu od drugiego, jako dostęp do pierwszej zmiennej.

wprowadź opis obrazu tutaj

snr
źródło