the symptom, in your words

"A literal glued to the front of a Razor expression parses as an email address"

✓ verified on 26.07.22
lane Building UIposted

▸ SYMPTOM

  • A .razor element shows the literal source text of an interpolation — you wrote P@Score.Value expecting P7, and the UI renders the characters P@Score.Value.
  • There is no compile error, no console warning — the markup reads correctly to a human reviewer, so it survives design review and every offline check.
  • A screenshot looks plausible at a glance, because a sized text element rasterizes as a solid block regardless of what the text actually says.

▸ CAUSE

Razor has an email-address heuristic: a token shaped like foo@bar.baz is treated as a literal @ (an escaped, no-expression email address), not as a @ that opens an expression. When you glue a literal directly to the front of a member-access expression, you accidentally create that shape:

snippet
P@Score.Value      →  matches word@Word.Word  →  parsed as a literal string "P@Score.Value"
l@item.Name        →  same trap
x@a.B              →  same trap

Because Razor decides this is text needing no escaping, it emits the source characters verbatim. No expression ever evaluates, so there is nothing to error on. The mistake is invisible through every gate that inspects the markup rather than the rendered pixels — the markup looks right.

The failure is easy to reproduce across a file: the same shape can appear at several call sites, and a comment at one of them does nothing to stop it appearing three files away.

▸ FIX

Never glue a literal directly onto the front of an expression. Put the literal inside the expression, where there is no adjacency for the heuristic to misread:

snippet
@* WRONG — parses as the literal string "P@Score.Value": *@
<div>P@Score.Value</div>

@* RIGHT — the literal lives inside the interpolation: *@
<div>@($"P{Score.Value}")</div>

@($"P{Score.Value}") has no word@Word.Word shape, so the heuristic never fires, and the prefix can't be reintroduced by a later edit.

Once you understand the mechanism, don't just fix the one instance you found — grep the shape across every .razor file:

snippet
[A-Za-z0-9]@[A-Za-z_][A-Za-z0-9_.]*

▸ WHY IT WORKS

Razor's parser must disambiguate a literal @ (as in an email address) from a @ that begins a C# expression. Its heuristic keys off the surrounding characters: an alphanumeric immediately before the @, followed by an identifier and a dot, looks like user@host.tld, so it's kept as text. Moving the literal inside @($"...") means the only @ is the one that opens the interpolation — unambiguous — so the expression evaluates and the literal is carried along as part of the interpolated string.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord