Class ValidationArgs
- Namespace
- Trellis
- Assembly
- Trellis.Core.dll
Builds the Args dictionary carried by a FieldViolation or
RuleViolation.
public static class ValidationArgs
- Inheritance
-
ValidationArgs
- Inherited Members
Remarks
Args are the machine-readable operands of a violation — the 50 in "must be at most 50",
the 0 and 255 in "must be between 0 and 255". A client that has them can render its
own localized message; a client that has only the English detail string cannot.
The values are ValidationArgValue — a closed union of text, number, boolean, and
list —
rather than object. A number therefore reaches the wire as a number, so a client
can compare it without parsing, while the union still denies a producer the chance to hand over
an arbitrary object and let culture-sensitive formatting leak into the payload. The numeric
conversions are implicit, so ValidationArgs.Of("max", 255) needs no ceremony.
There is deliberately no IFormattable overload. Adding one would make
ValidationArgs.Of("max", 255) ambiguous — an int converts to
IFormattable by boxing and to ValidationArgValue by a user-defined
conversion, and neither target is better than the other — so every numeric call site would stop
compiling. Its absence is what lets the implicit conversions bind. A value with no numeric or
textual meaning of its own, such as a timestamp, must therefore be formatted explicitly and
invariantly by the caller.
Args are for operands, not for echoing what the caller sent. Never put the rejected value itself in here — a rejected password or token would then be reflected back in an error response and into whatever logs that response.
Fields
MaxAllowedMembers
The largest member list Allowed(IEnumerable<string>) will publish.
public const int MaxAllowedMembers = 64
Field Value
Remarks
A list a client cannot act on is not worth what it costs to send. The 248 ISO country names serialize to roughly 3 KB, attached to every rejection; a request carrying several invalid enum fields multiplies that, so a small request provokes a large response, which is an amplification vector rather than mere waste. Past a few dozen options a client is not rendering "choose one of…" from an error payload anyway — it wants a schema or an enumeration endpoint, and the error is the wrong channel.
The bound is a member count rather than a serialized length because member names are
identifiers in every producer — CLR enum names, or the static field names behind a
RequiredEnum — so their length is already bounded in practice, and a count is
something a client can be told and can predict.
Methods
Allowed(IEnumerable<string>)
Builds the allowed entry naming the members a symbolic value may take.
public static ImmutableDictionary<string, ValidationArgValue> Allowed(IEnumerable<string> names)
Parameters
namesIEnumerable<string>The permitted member names. May be empty.
Returns
Remarks
Every producer that rejects a value for not being one of a fixed set routes through here,
so none of them can disagree about the entry's name, the order of its members, or the point
at which the list becomes too long to send. The producers derive their members from
unrelated places — GetNames(Type) for query binding, a
registry of declared statics for RequiredEnum — and nothing else would force those
to line up. A client that compares the list across producers, or caches it, must not see a
difference that is only ordering.
Ordinal sorting is what makes the order total and culture-independent; sorting by the current culture would let the same enum serialize differently on two machines.
Beyond MaxAllowedMembers the list is dropped whole and replaced by an
allowedCount entry. Truncating instead would publish a false statement: a client
cannot tell a shortened list from a complete one, so it would render "choose one of…" over a
wrong set and reject valid input when validating against it. Omission is already a case
every client handles, since a blank value carries no list either; the count is what
distinguishes "too many to send" from "not applicable here".
Of(string, ValidationArgValue)
Builds a single-entry args dictionary.
public static ImmutableDictionary<string, ValidationArgValue> Of(string name, ValidationArgValue value)
Parameters
namestringThe operand name, in
camelCase.valueValidationArgValueThe operand value.
Returns
Of(string, ValidationArgValue, string, ValidationArgValue)
Builds a two-entry args dictionary.
public static ImmutableDictionary<string, ValidationArgValue> Of(string name1, ValidationArgValue value1, string name2, ValidationArgValue value2)
Parameters
name1stringThe first operand name, in
camelCase.value1ValidationArgValueThe first operand value.
name2stringThe second operand name, in
camelCase.value2ValidationArgValueThe second operand value.
Returns
Of(params (string Name, ValidationArgValue Value)[])
Builds an args dictionary of any size.
public static ImmutableDictionary<string, ValidationArgValue> Of(params (string Name, ValidationArgValue Value)[] pairs)
Parameters
pairs(string Name, ValidationArgValue Value)[]The operand names, in
camelCase, paired with their values.
Returns
Remarks
A rule with three or more operands is ordinary — a scale-and-precision failure carries four — and without this overload a producer that needed one had to abandon ValidationArgs and assemble the dictionary by hand.