using System.ComponentModel.DataAnnotations.Resources; using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.ComponentModel.DataAnnotations { /// /// Validation attribute to assert a string property, field or parameter does not exceed a maximum length /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")] public class StringLengthAttribute : ValidationAttribute { /// /// Gets the maximum acceptable length of the string /// public int MaximumLength { get; private set; } /// /// Gets or sets the minimum acceptable length of the string /// public int MinimumLength { get; set; } /// /// Constructor that accepts the maximum length of the string. /// /// The maximum length, inclusive. It may not be negative. public StringLengthAttribute(int maximumLength) : base(() => DataAnnotationsResources.StringLengthAttribute_ValidationError) { this.MaximumLength = maximumLength; } /// /// Override of /// /// This method returns true if the is null. /// It is assumed the is used if the value may not be null. /// The value to test. /// true if the value is null or less than or equal to the set maximum length /// is thrown if the current attribute is ill-formed. internal override bool IsValid(object value) { // Check the lengths for legality this.EnsureLegalLengths(); // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. // We expect a cast exception if a non-string was passed in. int length = value == null ? 0 : ((string)value).Length; return value == null || (length >= this.MinimumLength && length <= this.MaximumLength); } /// /// Override of /// /// The name to include in the formatted string /// A localized string to describe the maximum acceptable length /// is thrown if the current attribute is ill-formed. public override string FormatErrorMessage(string name) { this.EnsureLegalLengths(); bool useErrorMessageWithMinimum = this.MinimumLength != 0 && !this.CustomErrorMessageSet; string errorMessage = useErrorMessageWithMinimum ? DataAnnotationsResources.StringLengthAttribute_ValidationErrorIncludingMinimum : this.ErrorMessageString; // it's ok to pass in the minLength even for the error message without a {2} param since String.Format will just // ignore extra arguments return String.Format(CultureInfo.CurrentCulture, errorMessage, name, this.MaximumLength, this.MinimumLength); } /// /// Checks that MinimumLength and MaximumLength have legal values. Throws InvalidOperationException if not. /// private void EnsureLegalLengths() { if (this.MaximumLength < 0) { throw new InvalidOperationException(DataAnnotationsResources.StringLengthAttribute_InvalidMaxLength); } if (this.MaximumLength < this.MinimumLength) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.RangeAttribute_MinGreaterThanMax, this.MaximumLength, this.MinimumLength)); } } } }