-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Closed
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.ComponentModel.DataAnnotationspartner-impactThis issue impacts a partner who needs to be kept updatedThis issue impacts a partner who needs to be kept updated
Milestone
Description
Background and motivation
Our project features hundreds of different option types against which we perform data validation operations. We've introduced a number of new data validation attributes which capture essential scenarios in our code. As these are general-purpose in nature, it would be great to add those to the core set of supported attributes.
API Proposal
namespace System.ComponentModel.Annotations;
public partial class RangeAttribute : ValidationAttribute
{
+ public bool IsLowerBoundExclusive { get; set; } = false;
+ public bool IsUpperBoundExclusive { get; set; } = false;
}
public partial class RequiredAttribute : ValidationAttribute
{
// Fail validation for structs that equal the default value for the type
+ public bool DisallowDefaultValues { get; set; } = false;
}
// Validates that the specified string uses Base64 encoding
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
+public class Base64StringAttribute : ValidationAttribute
+{
+}
// Specifies length ranges for string/IEnumerable
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
+public class LengthAttribute : ValidationAttribute
+{
+ public LengthAttribute(int minLength = 0, int maxLength = -1);
+
+ public int MinLength { get; }
+ public int? MaxLength { get; }
+}
// Validation using allow and deny lists
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
+public class AllowedValuesAttribute : ValidationAttribute
+{
+ public AllowedValuesAttribute (params object[] allowedValues);
+ public object[] AllowedValues { get; }
+}
+
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
+public class DeniedValuesAttribute : ValidationAttribute
+{
+ public DeniedValuesAttribute(params object[] deniedValues);
+ public object[] DeniedValues { get; }
+}Original API proposal
namespace System.ComponentModel.Annotations;
/// <summary>
/// Marks a property to be validated as <see href="https://en.wikipedia.org/wiki/Base64">Base64</see> string.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class Base64StringAttribute : ValidationAttribute
{
}
/// <summary>
/// Provides exclusive boundary validation for <see cref="long"/> or <see cref="double"/> values.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class ExclusiveRangeAttribute : ValidationAttribute
{
/// <summary>
/// Gets the minimum value for the range.
/// </summary>
public object Minimum { get; }
/// <summary>
/// Gets the maximum value for the range.
/// </summary>
public object Maximum { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ExclusiveRangeAttribute"/> class.
/// </summary>
/// <param name="minimum">The minimum value, exclusive.</param>
/// <param name="maximum">The maximum value, exclusive.</param>
public ExclusiveRangeAttribute(int minimum, int maximum);
/// <summary>
/// Initializes a new instance of the <see cref="ExclusiveRangeAttribute"/> class.
/// </summary>
/// <param name="minimum">The minimum value, exclusive.</param>
/// <param name="maximum">The maximum value, exclusive.</param>
public ExclusiveRangeAttribute(double minimum, double maximum);
}
/// <summary>
/// Specifies the minimum length of any <see cref="IEnumerable"/> or <see cref="string"/> objects.
/// </summary>
/// <remarks>
/// The standard <see cref="MinLengthAttribute" /> supports only non generic <see cref="Array"/> or <see cref="string"/> typed objects
/// on .Net Framework, while <see cref="ICollection{T}"/> type is supported only on .Net Core.
/// See issue here <see href="https://github.com/dotnet/runtime/issues/23288"/>.
/// This attribute aims to allow validation of all these objects in a consistent manner across target frameworks.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class LengthAttribute : ValidationAttribute
{
/// <summary>
/// Gets the minimum allowed length of the collection or string.
/// </summary>
public int MinimumLength { get; }
/// <summary>
/// Gets the maximum allowed length of the collection or string.
/// </summary>
public int? MaximumLength { get; }
/// <summary>
/// Gets or sets a value indicating whether the length validation should exclude the <see cref="MinimumLength"/> and <see cref="MaximumLength"/> values.
/// </summary>
/// <remarks>
/// By default the property is set to <c>false</c>.
/// </remarks>
public bool Exclusive { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="LengthAttribute"/> class.
/// </summary>
/// <param name="minimumLength">
/// The minimum allowable length of array/string data.
/// Value must be greater than or equal to zero.
/// </param>
public LengthAttribute(int minimumLength);
/// <summary>
/// Initializes a new instance of the <see cref="LengthAttribute"/> class.
/// </summary>
/// <param name="minimumLength">
/// The minimum allowable length of array/string data.
/// Value must be greater than or equal to zero.
/// </param>
/// <param name="maximumLength">
/// The maximum allowable length of array/string data.
/// Value must be greater than or equal to zero.
/// </param>
public LengthAttribute(int minimumLength, int maximumLength);
}
/// <summary>
/// Provides a way of requiring a value that is not default(T) for a property of value type.
/// </summary>
/// <remarks>Only to be used with value type properties.</remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class NonDefaultValueRequiredAttribute : ValidationAttribute
{
}
/// <summary>
/// Provides a way of not accepting specific values for a property/field/parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class RejectedValuesAttribute : ValidationAttribute
{
/// <summary>
/// Gets the values which are not accepted.
/// </summary>
public ICollection<object> RejectedValues { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RejectedValuesAttribute"/> class.
/// </summary>
/// <param name="rejectedValues">Values which should not be accepted.</param>
public RejectedValuesAttribute(params object[] rejectedValues);
}
/// <summary>
/// Provides a way of accepting only specific values for a property/field/parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class AcceptedValuesAttribute : ValidationAttribute
{
/// <summary>
/// Gets the values which are accepted.
/// </summary>
public ICollection<object> AcceptedValues { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AcceptedValuesAttribute"/> class.
/// </summary>
/// <param name="acceptedValues">Values which should be accepted.</param>
public AcceptedValuesAttribute(params object[] acceptededValues);
}
/// <summary>
/// Provides boundary validation for <see cref="TimeSpan"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class TimeSpanAttribute : ValidationAttribute
{
/// <summary>
/// Gets the lower bound for time span.
/// </summary>
public TimeSpan Minimum { get; }
/// <summary>
/// Gets the upper bound for time span.
/// </summary>
public TimeSpan? Maximum { get; }
/// <summary>
/// Gets or sets a value indicating whether the time span validation should exclude the minimum and maximum values.
/// </summary>
/// <remarks>
/// By default the property is set to <c>false</c>.
/// </remarks>
public bool Exclusive { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="minMs">Minimum in milliseconds.</param>
public TimeSpanAttribute(int minMs);
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="minMs">Minimum in milliseconds.</param>
/// <param name="maxMs">Maximum in milliseconds.</param>
public TimeSpanAttribute(int minMs, int maxMs);
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="min">Minimum represented as time span string.</param>
public TimeSpanAttribute(string min);
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="min">Minimum represented as time span string.</param>
/// <param name="max">Maximum represented as time span string.</param>
public TimeSpanAttribute(string min, string max);
}API Usage
class Options
{
// require a valid base-64 string
[Base64String]
public string EncodedBinaryData { get; set; } = string.Empty;
// a value between specific exclusive boundaries
[ExclusiveRange(0, 1.0)]
public double BetweenZeroAndOne { get; set; } = .5;
// 1 to 5 items
[Length(1, 5)]
public int[] ArrayOfStuff { get; set; }
// anything but these
[RejectValues("Red")]
public string Color { get; set; }
// 10ms to 100ms
[TimeSpan(10, 100)]
public TimeSpan Timeout { get; set; }
}Alternative Designs
No response
Risks
No response
Metadata
Metadata
Assignees
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.ComponentModel.DataAnnotationspartner-impactThis issue impacts a partner who needs to be kept updatedThis issue impacts a partner who needs to be kept updated