Part of DKNet.Svc.Transformation. Transform/TransformAsync extract every token in the template, resolve each one against the parameters you pass, and rebuild the string in a single pass.
var order = new { OrderId = 4711, Total = 129.5m, Placed = new DateTime(2026, 3, 1), Paid = true };
var text = transformer.Transform("Order [OrderId] — [Total] on [Placed]. Paid: [Paid]", order);
// Order 4,711 — 129.50 on 01/03/2026 12.00.00. Paid: Yes
Resolution walks the parameters array in order, then falls back to TransformOptions.GlobalParameters (set at registration). For each entry: an IDictionary<string, string> passed directly as a Transform/TransformAsync argument is looked up by key, case-insensitively; an IEnumerable<object> — including GlobalParameters itself — is searched recursively, item by item, and its items resolve by property only, so a dictionary inside a collection or in GlobalParameters is not looked up by key; anything else is treated as an object — a public instance property matching the token name wins, and a non-public one is tried next. The first non-null value wins.
This does not show the not-found policy (below) or value formatting, which run after resolution for every token regardless of which source supplied the value.
public enum TokenNotFoundBehavior { LeaveAsIs, Remove, ThrowError } // default: ThrowError
ThrowError (the default) throws UnResolvedTokenException on the first token nothing can resolve. LeaveAsIs keeps the token text verbatim. Remove replaces it with an empty string. A property that exists but holds null counts as unresolved — there is no way to distinguish “missing” from “null” here.
| Option | Type | Default | Effect |
|---|---|---|---|
TokenNotFoundBehavior |
TokenNotFoundBehavior |
ThrowError |
LeaveAsIs / Remove for lenient rendering instead of throwing UnResolvedTokenException. |
GlobalParameters |
IEnumerable<object> |
[] |
Fallback resolution sources tried after the call’s own parameters. Configured at registration. |
IDictionary<string, string>. Any other IDictionary throws ArgumentException during resolution — including Dictionary<string, object>.[Customer.Name] is looked up as a single property literally named Customer.Name, which normally fails; flatten your data (project to an anonymous object or dictionary) first.Transform/TransformAsync call — a token repeated twice in the same template resolves once, but nothing is ever shared across calls or across a reused ITransformerService instance.