Working with Tables

Use table-row data bands, table-column data bands, and cross (pivot) tables in LINQ Reporting templates.
What is this page about?

This page covers table-oriented reporting patterns used in production templates.

It explains row data bands, column data bands, and cross (pivot) table layouts.

Working with Table-Row Data Bands

A table-row data band repeats a full table row for each item in a collection.

Place the opening and closing foreach tags in the same row, then bind cell content to the current item.

Template (conceptual row inside a table):

| <<foreach [segment in booking.Segments.Where(s => s.IsConfirmed)]>> <<[segment.Carrier]>> | <<[segment.From]>> | <<[segment.To]>> | <<[segment.DepartureUtc]:"dd MMM HH:mm">><</foreach>> |

Expected output:

| Pacific Air | Seattle | Tokyo | 14 Jun 09:20 |
| Pacific Air | Tokyo | Singapore | 18 Jun 13:05 |

Use this pattern for manifests, schedules, invoice lines, and any one-record-per-row layout.

Working with Table-Column Data Bands

A table-column data band repeats a column for each item in a collection. This is useful when entities should expand horizontally.

Use the horizontal foreach switch and keep the repeated content in one template column.

Template (conceptual table):

| Traveler | <<foreach [traveler in booking.Travelers] -horz>><<[traveler.FullName]>><</foreach>> |
| Seat     | <<foreach [traveler in booking.Travelers] -horz>><<[traveler.Seat ?? "pending"]>><</foreach>> |

Expected output:

| Traveler | Avery Chen | Noah Patel |
| Seat     | 12A        | pending    |

Use this pattern when the number of compared entities is small and fixed-width horizontal growth is acceptable.

Working with Cross (Pivot) Tables

Cross (pivot) tables combine a row axis and a column axis to summarize many records compactly.

In templates, this is usually modeled by:

  • generating one header column per key on the horizontal axis
  • repeating body rows for the vertical axis
  • resolving each body cell from filtered data

Conceptual pattern:

<<var [segmentsDates = booking.Segments.Select(s => s.DepartureUtc.ToString("yyyy-MM-dd")).Distinct().OrderBy(x => x)]>>

| Date | <<foreach [m in segmentsDates] -horz>> <<[m]>><</foreach>> |
| <<foreach [carrier in booking.Segments.Select(s => s.Carrier).Distinct().OrderBy(x => x)]>><<[carrier]>> | <<foreach [m in segmentsDates] -horz>> <<[booking.Segments.Count(s => s.Carrier == carrier && s.DepartureUtc.ToString("yyyy-MM-dd") == m)]>><</foreach>><</foreach>> |

Expected output with the shared sample data:

| Date            | 2026-06-14 | 2026-06-18 | 2026-06-21 |
| Harbor Jet      | 0          | 0          | 1          |
| Pacific Air     | 1          | 1          | 0          |

For large pivots, precompute row keys, column keys, and cell aggregates in application code, then bind a pre-shaped view model.

Important to note:
For complex table scenarios, shape the data in application code first. Templates remain easier to validate when they focus on rendering, not heavy aggregation.