How it works
A query expression is another syntax for composing LINQ operations. A let clause gives a name to an intermediate calculation so later clauses can reuse it. The sample trims and invariant-lowercases each name once for that query element, then uses the normalized value for filtering, ordering, and projection.
C# becomes c# after trimming and has length two, so the where clause excludes it. LINQ and Rune both have four characters after normalization. Ordering by the normalized text produces linq before rune, and the projection includes each normalized length to make the shared intermediate value visible.
Naming a calculation improves clarity when several later steps depend on it. It also reduces accidental inconsistency, such as filtering with trimmed text but displaying an untrimmed length. The let clause does not make the whole query eager; ordinary LINQ-to-Objects execution still occurs when the result is enumerated.
A common mistake is treating query syntax as a separate database language with identical behavior for every provider. This example runs over an in-memory array through LINQ to Objects. Other query providers can have translation limits and different execution locations, so do not assume an arbitrary C# method in a clause will translate to another system.
Practice by adding a second let clause for the first character of the nonempty normalized name. Make the empty-string case safe before indexing. Then rewrite the same query with Select, Where, OrderBy, and Select method calls, comparing outputs and identifying where the intermediate values are carried.
Finally, choose whichever syntax makes this particular pipeline easier to review. Query syntax is especially readable for some joins and multiple sources, while method syntax is convenient for operators without query keywords. Consistency should support understanding rather than force an opaque expression; retain explicit equality, ordering, and materialization decisions in either form.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
string[] names = { " Rune ", " C# ", "LINQ" };
var report = from name in names
let normalized = name.Trim().ToLowerInvariant()
where normalized.Length > 3
orderby normalized
select $"{normalized}:{normalized.Length}";
Console.WriteLine(string.Join(",", report));
Expected output
linq:4,rune:4
let gives an intermediate value a name for later clauses.