Queries and Control Flow
This page focuses on the expression patterns that turn raw data into readable report output.
It covers filtering, sorting, grouping, conditional regions, nested loops, and everyday formatting work with strings, numbers, dates, and null values.
The LINQ Reporting Engine evaluates C#-style expressions inside tags, which means you can shape collections close to the place where they are displayed.
Filter and Sort a Collection
Example template:
Confirmed segments
<<foreach [segment in booking.Segments.Where(s => s.IsConfirmed).OrderBy(s => s.DepartureUtc)]>>
- <<[segment.From]>> to <<[segment.To]>> on <<[segment.DepartureUtc]:"dd MMM HH:mm">>
<</foreach>>Expected output:
Confirmed segments
- Seattle to Tokyo on 14 Jun 09:20
- Tokyo to Singapore on 18 Jun 13:05Project a Simpler View
Projection is useful when the document only needs a small part of a larger object:
Visited cities:
<<foreach [city in booking.Segments.Select(s => s.To).Distinct()]>>- <<[city]>>
<</foreach>>Expected output:
Visited cities:
- Tokyo
- Singapore
- SydneyGroup Repeated Content
Grouping is effective when the report should show headings with detail lines beneath each heading:
<<foreach [carrierGroup in booking.Segments.GroupBy(s => s.Carrier).OrderBy(g => g.Key)]>>
Carrier: <<[carrierGroup.Key]>>
<<foreach [segment in carrierGroup]>>- <<[segment.From]>> to <<[segment.To]>>
<</foreach>>
<</foreach>>Expected output:
Carrier: Pacific Air
- Seattle to Tokyo
- Tokyo to Singapore
Carrier: Harbor Jet
- Singapore to SydneySwitch Layouts With Conditions
Conditional blocks let the template describe multiple presentation paths for the same record:
<<if [booking.UpgradeOffer != null]>>
Upgrade offer: <<[booking.UpgradeOffer]>>
<<else>>
Upgrade offer: Not available for this fare.
<</if>>This pattern is usually easier to maintain than scattering null checks into every output expression.
Nest Regions Carefully
Nested foreach blocks are common in itinerary, order, and scheduling reports:
<<foreach [traveler in booking.Travelers]>>
Traveler: <<[traveler.FullName]>>
<<foreach [segment in traveler.PersonalSegments]>>- <<[segment.From]>> to <<[segment.To]>>
<</foreach>>
<</foreach>>If nesting becomes hard to follow, add tag headers and keep the inner blocks visually indented in the template document.
Strings, Numbers, Dates, and Nulls
Common formatting patterns:
Passenger count: <<[booking.Travelers.Count()]>>
Grand total: <<[booking.TotalAmount]:"0.00">> USD
Departure date: <<[booking.DepartureDate]:"dddd, dd MMM yyyy">>
<<foreach [traveler in booking.Travelers]>>
<<[traveler.FullName]>>: seat <<[traveler.Seat ?? "pending assignment"]>>
<</foreach>>Expected output:
Passenger count: 2
Grand total: 1840.50 USD
Departure date: Sunday, 14 Jun 2026
Avery Chen: seat 12A
Noah Patel: seat pending assignmentThese expressions keep light presentation logic in the template while leaving the underlying calculations in application code.
Collections and Dictionary Lookups
Collection queries and dictionary access can be mixed in a single expression when the model supports it:
Support language: <<[booking.Metadata["SupportLanguage"]]>>
Meal requests: <<[booking.Travelers.Count(t => t.MealPreference != null)]>>