Language Integrated Query (LINQ) is a C# feature for querying data with a common syntax. In an ASP.NET Core service, it can turn a collection of reflected properties into the names returned by an endpoint. The same approach can later query a database, but the example here operates on an array. section 3.1.1 section 3.3
Listing 3.8 Property getting all fonts with reflection
public static class AsciiArt
{
public static Lazy<IEnumerable<string>> AllFonts = #1
new (() => #2
from p in typeof(FiggleFonts) #3
.GetProperties( #4
BindingFlags.Public | BindingFlags.Static)
select p.Name); #5
// ...
}
The query starts with the source: public static properties returned by reflection. `select p.Name` projects each property into its name. Placing `from` before `select` also lets an editor know the source while suggesting available members. section 3.1.1
LINQ query syntax is not a separate collection engine. The compiler translates this projection into a method call resembling `GetProperties(...).Select(p => p.Name)`. The returned array implements `IEnumerable`, the collection interface used here to enumerate its elements. `Lazy` delays creation of the collection until its value is first requested; for this small font list, that optimization is optional. section 3.1.1
Listing 3.9 LINQ query translated to methods
public static Lazy<IEnumerable<string>> AllFonts =
new (() =>
typeof(FiggleFonts) #1
.GetProperties(
BindingFlags.Public | BindingFlags.Static)
.Select(p => p.Name)); #2
Compare the method form with the query above: the lambda `p => p.Name` performs the same projection as `select p.Name`. Choose the form that makes the surrounding service code easiest to follow. section 3.1.1
The collection becomes more useful as the endpoint accepts query parameters:
- Return `AllFonts.Value` from a root `MapGet` handler to expose the names. section 3.1.1
- Apply `Skip(skip ?? 0)` and `Take(take ?? 100)` to provide optional paging with defaults. section 3.1.1
- For richer results, project each property into a name-and-font tuple, then filter by name or direction and order by name. section 3.1.1
Listing 3.14 Adding paging, sorting, and filtering to font list
using Figgle; #1
const StringComparison SCIC = #2
StringComparison.OrdinalIgnoreCase; #2
app.MapGet("/",
(
int? skip,
int? take,
FiggleTextDirection? dir, #3
string? name, #4
string? order #5
) => {
var query = from f in AsciiArt.AllFonts.Value #6
where #7
(name == null || f.Name.Contains(name, SCIC)) #8
&& (dir == null || f.Font.Direction == dir) #9
select f;
if (string.Equals("desc", order, SCIC)) #10
{
query = query.OrderByDescending(f => f.Name); #11
}
else
{
query = query.OrderBy(f => f.Name); #11
}
return query #12
.Skip(skip ?? 0)
.Take(take ?? 200)
.Select(f => f.Name);
});
The service query combines optional filters before choosing an ordering. Its name check uses an ordinal, case-insensitive comparison, so filtering does not depend on the caller matching letter case. section 3.1.1


