Skip to content

Attributes ​

This page describes each attribute in the ValidationModules.Constraints namespace. For how the attributes fit together, see Constraint attributes.

In the messages below, {field} is the last segment of the error's field path, or the property's [Display(Name)] label when it has one. Numbers and dates in a message are formatted with the invariant culture.

Properties every constraint has ​

Every constraint attribute derives from ValidationConstraintAttribute, which has four properties:

PropertyEffect
CodeReplaces the error code. The default message is kept.
MessageReplaces the message with literal text. {field} is the only placeholder. On a built-in attribute or a CustomConstraintAttribute, the text is authored, so language packs do not replace it.
WhenThe name of a member of the model. The constraint applies only when it is true.
UnlessThe name of a member of the model. The constraint applies only when it is false.

When and Unless accept a bool property, a parameterless method that returns bool, or a static method that takes the model and returns bool. A constraint cannot set both.

ValidationModules_CodeNamespace adds a prefix to a code set with Code on a built-in attribute, a CustomConstraintAttribute or an attribute that implements IConstraintFor<T>, as in myapp.weight_out_of_range. Built-in codes are never prefixed.

Every constraint except [Required] passes a null value. It also passes a default ImmutableArray<T>, which has no array behind it. When [Required] fails, the other constraints on the property are skipped.

Presence ​

[Required] ​

csharp
public RequiredAttribute();

public bool AllowEmptyStrings { get; init; }

[Required] passes when the value is not null. On a string, the value must also not be empty or whitespace, unless AllowEmptyStrings is true. On a collection only null fails. On an ImmutableArray<T>, a default array fails and an empty one passes. On an ImmutableArray<T>?, null and a default array fail.

CodeMessage
required{field} is required.

On a property of any other non-nullable value type, such as int, [Required] can never fail. The generator reports VM1201 and drops it.

Strings ​

[StringLength] ​

csharp
public StringLengthAttribute();

public StringLengthAttribute(int max);

public int Min { get; init; }
public int Max { get; init; }

[StringLength] passes when the length of the string is between Min and Max, inclusive. Applies to string only. The length is string.Length, which counts UTF-16 code units, so a character outside the Basic Multilingual Plane, such as most emoji, counts as two.

The constructor argument is the maximum, as it is for the DataAnnotations [StringLength], so [StringLength(50)] means at most 50 characters. Set a minimum by name, as in [StringLength(50, Min = 2)], or [StringLength(Min = 2)] for a minimum with no maximum.

CodeMessage
string_length{field} must be between {0} and {1} characters.
string_length{field} must be at least {0} characters. when only Min is set
string_length{field} must be at most {0} characters. when only Max is set

When the deciding bound is 1, the message says character. Diagnostics: VM1001 on a property that is not a string, and VM1101 when Min is greater than Max.

[Pattern] ​

csharp
public PatternAttribute(string pattern);

public PatternAttribute(Type regexProvider, string regexMember);

public RegexOptions Options { get; init; }
public int MatchTimeoutMilliseconds { get; init; }

[Pattern] passes when the regular expression matches the string. The match can be anywhere in the value, so anchor the expression with ^ and $ to match all of it. Applies to string only.

The first constructor takes the expression. The second names a static member of type Regex on another type, usually a [GeneratedRegex] method. Options and MatchTimeoutMilliseconds apply to the first form only. A match that runs past its timeout fails the pattern rather than throwing. See Patterns.

CodeMessage
pattern{field} is not in the required format.

Diagnostics: VM1001 on a property that is not a string, VM1106 when the expression does not parse, VM1107 when the referenced member cannot be used, VM1301 for an inline expression under the pattern policy, VM1302 when the first form's Options includes RegexOptions.Compiled, VM1303 when the second form sets Options or MatchTimeoutMilliseconds, and VM1304 when the first form's MatchTimeoutMilliseconds is a value the Regex constructor rejects.

[EmailAddress] ​

csharp
public EmailAddressAttribute();

[EmailAddress] passes when the string contains exactly one @, which is neither the first nor the last character, and no line breaks. This is the same rule as the DataAnnotations attribute. It accepts a@b.

CodeMessage
email{field} is not a valid email address.

[Phone] ​

csharp
public PhoneAttribute();

[Phone] applies the DataAnnotations phone rule. + signs and a trailing extension such as ext. 12 or x12 are removed, and the rest must contain a digit and only digits, whitespace, -, ., ( and ).

CodeMessage
phone{field} is not a valid phone number.

[Url] ​

csharp
public UrlAttribute();

On a string, [Url] passes when the value starts with http://, https:// or ftp://, ignoring case. The rest of the value is not checked. On a Uri, passes when the URI is absolute and its scheme is http, https or ftp.

CodeMessage
url{field} is not a valid http, https or ftp URL.

[CreditCard] ​

csharp
public CreditCardAttribute();

[CreditCard] passes when the digits pass the Luhn checksum. Spaces and dashes are ignored, and any other character fails. An empty string passes, so combine it with [Required].

CodeMessage
credit_card{field} is not a valid credit card number.

[Base64String] ​

csharp
public Base64StringAttribute();

[Base64String] passes when the string is valid Base64. Whitespace is allowed.

CodeMessage
base64{field} is not a valid Base64 string.

[FileExtensions] ​

csharp
public FileExtensionsAttribute();

public string? Extensions { get; init; }

[FileExtensions] passes when the file name's extension is in Extensions, a comma-separated list such as "pdf,docx". The comparison ignores case. The default list is png,jpg,jpeg,gif. Spaces and dots in the list are removed, so tar.gz is read as targz.

CodeMessage
file_extension{field} must have one of these file extensions: {0}. with the list, as in .pdf, .docx

Numbers, dates and times ​

[Range] ​

csharp
public RangeAttribute();

public RangeAttribute(int min, int max);

public RangeAttribute(long min, long max);

public RangeAttribute(double min, double max);

public RangeAttribute(string min, string max);

public object? Min { get; init; }
public object? Max { get; init; }
public bool ExclusiveMin { get; init; }
public bool ExclusiveMax { get; init; }

[Range] passes when the value is between Min and Max. Both bounds are inclusive unless ExclusiveMin or ExclusiveMax is set, and either can be left out. Applies to the integral types, float, double, decimal, DateTime, DateOnly, TimeOnly, TimeSpan, DateTimeOffset, and their nullable forms.

The bounds are converted to the property's type at build time. String bounds are parsed with the invariant culture, as in [Range("2024-01-01", "2030-12-31")] on a DateOnly. A DateTime or DateOnly bound written without a time zone is compared as written. A DateTime bound written with Z or an offset keeps its instant, converted to UTC and compared with DateTimeKind.Utc. A DateTimeOffset bound written without an offset is read as UTC. The parsed bounds do not depend on the machine that builds the project.

CodeMessage
range{field} must be between {0} and {1}.
range{field} must be greater than {0} and at most {1}. with ExclusiveMin
range{field} must be at least {0} and less than {1}. with ExclusiveMax
range{field} must be greater than {0} and less than {1}. with both
range{field} must be at least {0}. or {field} must be greater than {0}. with only Min
range{field} must be at most {0}. or {field} must be less than {0}. with only Max

Diagnostics: VM1003 on a type with no ordering, VM1102 when neither bound is set, VM1103 when a bound does not parse as the property's type, and VM1101 when no value can satisfy the bounds: Min greater than Max, or equal bounds with ExclusiveMin or ExclusiveMax set. The bounds are compared as the property's type, so date bounds compare as instants.

[MultipleOf] ​

csharp
public MultipleOfAttribute(int divisor);

public MultipleOfAttribute(long divisor);

public MultipleOfAttribute(double divisor);

public MultipleOfAttribute(string divisor);

public object Divisor { get; }

[MultipleOf] passes when the value divides by the divisor with no remainder. Applies to the integral types, decimal, double and float. For double and float, the check converts the value to decimal first, so 0.3 is a multiple of 0.1, and a value too large for decimal fails. On an integral property the divisor must be a whole number. Use the string constructor for an exact decimal divisor on a decimal property, as in [MultipleOf("0.05")].

CodeMessage
multiple_of{field} must be a multiple of {0}.

Diagnostics: VM1004 on a type that is not numeric, VM1104 when the divisor is zero or negative, and VM1105 when it does not fit the property's type.

Values and enums ​

[AllowedValues] ​

csharp
public AllowedValuesAttribute(params object[] values);

public object[] Values { get; }
public StringComparison Comparison { get; init; }

[AllowedValues] passes when the value equals one of Values. On an enum property the values can be enum members. Strings are compared with Comparison, which is ordinal and case-sensitive by default, so Comparison = StringComparison.OrdinalIgnoreCase accepts "ACTIVE" for "active". Comparison applies only to a string property.

CodeMessage
enum{field} must be one of: {0}. with the values, as in active, pending

Diagnostics: VM1203 when Comparison is set on a property that is not a string, and VM3109 when no values are listed.

[DeniedValues] ​

csharp
public DeniedValuesAttribute(params object[] values);

public object[] Values { get; }

[DeniedValues] passes when the value equals none of Values.

CodeMessage
enum{field} must not be one of: {0}.

[EnumDefined] ​

csharp
public EnumDefinedAttribute();

[EnumDefined] passes when the value is a member the enum declares. On a [Flags] enum, passes when the value is a combination of declared flags, and 0 always passes. The check compares against the members known at build time and does not call Enum.IsDefined.

CodeMessage
enum{field} must be one of: {0}. with the member names
enum{field} must be a combination of: {0}. for a [Flags] enum

Diagnostics: VM1006 on a property that is not an enum, or an enum with no members.

Collections ​

[ItemCount] ​

csharp
public ItemCountAttribute();

public ItemCountAttribute(int min = 0, int max = int.MaxValue);

public int Min { get; init; }
public int Max { get; init; }

[ItemCount] passes when the number of items is between Min and Max, inclusive. The first argument is the minimum. Applies to arrays, to collections with a public Count or Length property, including dictionaries, and to any other IEnumerable<T>. A sequence with neither property is counted with Enumerable.Count, so validation enumerates a lazy sequence once.

CodeMessage
array_bounds{field} must be between {0} and {1} items.
array_bounds{field} must be at least {0} items. when only Min is set
array_bounds{field} must be at most {0} items. when only Max is set

When the deciding bound is 1, the message says item. Diagnostics: VM1002 on a property that is not a collection, and VM1101 when Min is greater than Max.

[UniqueItems] ​

csharp
public UniqueItemsAttribute();

[UniqueItems] passes when no item appears twice, compared with the element type's default equality. Strings are compared ordinally. Applies to arrays and collections.

CodeMessage
unique_items{field} must not contain duplicate items.

Diagnostics: VM1005 on a property that is not a collection, and VM1202 when the element type compares by reference, so that two items with equal contents both pass. Make the element a record, override Equals, or implement IEquatable<T>.

Nesting ​

[ValidateNested] ​

csharp
public ValidateNestedAttribute();

public ValidateNestedAttribute(Polymorphism polymorphism);

public Polymorphism Polymorphism { get; }

[ValidateNested] runs the validators for the property's type on its value, or on every element of a collection, or on every value of a dictionary. Errors are reported under the property's path, as in shipTo.postcode, lines[1].sku or addresses[work].postcode. A null value is skipped, and so is a default ImmutableArray<T>. When and Unless decide whether the descent happens.

Polymorphism chooses the validators when the value can be of a derived type:

ValueValidators
Polymorphism.DeclaredOnlyThe declared type's validators only.
Polymorphism.CompileTimeThe validators of the value's actual type, chosen from the subtypes in the same project.
Polymorphism.RuntimeThe validators registered in the container for the value's actual type. The pass needs a service provider.

Diagnostics: VM1501 when the type has no rules, VM1502 when no validator can exist for the type, VM1503 when the type is not sealed and no Polymorphism is given, VM1504 for Runtime on a sealed or value type, and VM1505 when the type is declared in another assembly and this project can reach no validator for it. See Nested objects and collections.

Types ​

[GenerateValidator] ​

csharp
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
public GenerateValidatorAttribute();

[GenerateValidator] makes the generator write a validator for a type that has no constraints of its own. When the type has no rules from anywhere else, such as a rules class, the validator passes every value. Use it for a type that needs a registered IValidatorFor<T>, such as the target of [ValidateNested], of .Validate<T>() in ASP.NET Core, or of hand-written rules.

[PerValidationInstance] ​

csharp
[AttributeUsage(AttributeTargets.Class)]
public PerValidationInstanceAttribute();

[PerValidationInstance] goes on an attribute class that implements IConstraintFor<T>. The generator then creates a new instance of the attribute for every check, instead of one shared instance. Each use is reported as VM1603. See Custom constraints.

Base classes ​

ValidationConstraintAttribute is the base class of every constraint attribute, and the source of the four properties at the top of this page.

CustomConstraintAttribute is the base class for a constraint with a static IsValid method. See Custom constraints.

Released under the MIT License.