Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new System.ComponentModel.DataAnnotations features #82311

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

namespace System.ComponentModel.DataAnnotations
{
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple = false)]
[System.CLSCompliant(false)]
public partial class AllowedValuesAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
public AllowedValuesAttribute(params object?[] values) { }
public object?[] Values { get { throw null; } }
public override bool IsValid(object? value) { throw null; }
}
public partial class AssociatedMetadataTypeTypeDescriptionProvider : System.ComponentModel.TypeDescriptionProvider
{
public AssociatedMetadataTypeTypeDescriptionProvider(System.Type type) { }
Expand All @@ -24,6 +32,12 @@ public AssociationAttribute(string name, string thisKey, string otherKey) { }
public string ThisKey { get { throw null; } }
public System.Collections.Generic.IEnumerable<string> ThisKeyMembers { get { throw null; } }
}
[System.AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class Base64StringAttribute : ValidationAttribute
{
public Base64StringAttribute() { }
public override bool IsValid(object? value) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)]
public partial class CompareAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
Expand Down Expand Up @@ -87,6 +101,14 @@ public DataTypeAttribute(string customDataType) { }
public virtual string GetDataTypeName() { throw null; }
public override bool IsValid(object? value) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple = false)]
[System.CLSCompliant(false)]
public partial class DeniedValuesAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
public DeniedValuesAttribute(params object?[] values) { }
public object?[] Values { get { throw null; } }
public override bool IsValid(object? value) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Field | System.AttributeTargets.Method | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple=false)]
public sealed partial class DisplayAttribute : System.Attribute
{
Expand Down Expand Up @@ -183,6 +205,16 @@ public sealed partial class KeyAttribute : System.Attribute
{
public KeyAttribute() { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple = false)]
public partial class LengthAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Uses reflection to get the 'Count' property on types that don't implement ICollection. This 'Count' property may be trimmed. Ensure it is preserved.")]
public LengthAttribute(int minimumLength, int maximumLength) { }
public int MinimumLength { get { throw null; } }
public int MaximumLength { get { throw null; } }
public override string FormatErrorMessage(string name) { throw null; }
public override bool IsValid(object? value) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple=false)]
public partial class MaxLengthAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
Expand Down Expand Up @@ -225,7 +257,9 @@ public RangeAttribute(int minimum, int maximum) { }
public RangeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string minimum, string maximum) { }
public bool ConvertValueInInvariantCulture { get { throw null; } set { } }
public object Maximum { get { throw null; } }
public bool MaximumIsExclusive { get { throw null; } set { } }
public object Minimum { get { throw null; } }
public bool MinimumIsExclusive { get { throw null; } set { } }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)]
public System.Type OperandType { get { throw null; } }
public bool ParseLimitsInInvariantCulture { get { throw null; } set { } }
Expand All @@ -247,6 +281,7 @@ public partial class RequiredAttribute : System.ComponentModel.DataAnnotations.V
{
public RequiredAttribute() { }
public bool AllowEmptyStrings { get { throw null; } set { } }
public bool DisallowAllDefaultValues { get { throw null; } set { } }
public override bool IsValid(object? value) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple=false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AllowedValuesAttribute_Invalid" xml:space="preserve">
<value>The {0} field does not equal any of the values specified in AllowedValuesAttribute.</value>
</data>
<data name="ArgumentIsNullOrWhitespace" xml:space="preserve">
<value>The argument '{0}' cannot be null, empty or contain only whitespace.</value>
</data>
Expand All @@ -66,6 +69,9 @@
<data name="AttributeStore_Unknown_Property" xml:space="preserve">
<value>The type '{0}' does not contain a public property named '{1}'.</value>
</data>
<data name="Base64StringAttribute_Invalid" xml:space="preserve">
<value>The {0} field is not a valid Base64 encoding.</value>
</data>
<data name="Common_PropertyNotFound" xml:space="preserve">
<value>The property {0}.{1} could not be found.</value>
</data>
Expand Down Expand Up @@ -105,6 +111,9 @@
<data name="DataTypeAttribute_EmptyDataTypeString" xml:space="preserve">
<value>The custom DataType string cannot be null or empty.</value>
</data>
<data name="DeniedValuesAttribute_Invalid" xml:space="preserve">
<value>The {0} field equals one of the values specified in DeniedValuesAttribute.</value>
</data>
<data name="DisplayAttribute_PropertyNotSet" xml:space="preserve">
<value>The {0} property has not been set. Use the {1} method to get the value.</value>
</data>
Expand Down Expand Up @@ -138,6 +147,15 @@
<data name="MinLengthAttribute_ValidationError" xml:space="preserve">
<value>The field {0} must be a string or array type with a minimum length of '{1}'.</value>
</data>
<data name="LengthAttribute_InvalidMinLength" xml:space="preserve">
<value>LengthAttribute must have a MinimumLength value that is zero or greater.</value>
</data>
<data name="LengthAttribute_InvalidMaxLength" xml:space="preserve">
<value>LengthAttribute must have a MaximumLength value that is greater than or equal to MinimumLength.</value>
</data>
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
<data name="LengthAttribute_ValidationError" xml:space="preserve">
<value>The field {0} must be a string or collection type with a minimum length of '{1}' and maximum length of '{2}'.</value>
</data>
<data name="LengthAttribute_InvalidValueType" xml:space="preserve">
<value>The field of type {0} must be a string, array or ICollection type.</value>
</data>
Expand All @@ -150,6 +168,9 @@
<data name="RangeAttribute_MinGreaterThanMax" xml:space="preserve">
<value>The maximum value '{0}' must be greater than or equal to the minimum value '{1}'.</value>
</data>
<data name="RangeAttribute_CannotUseExclusiveBoundsWhenTheyAreEqual" xml:space="preserve">
<value>Cannot use exclusive bounds when the maximum value is equal to the minimum value.</value>
</data>
<data name="RangeAttribute_Must_Set_Min_And_Max" xml:space="preserve">
<value>The minimum and maximum values must be set.</value>
</data>
Expand All @@ -159,6 +180,15 @@
<data name="RangeAttribute_ValidationError" xml:space="preserve">
<value>The field {0} must be between {1} and {2}.</value>
</data>
<data name="RangeAttribute_ValidationError_MinExclusive" xml:space="preserve">
<value>The field {0} must be between {1} exclusive and {2}.</value>
</data>
<data name="RangeAttribute_ValidationError_MaxExclusive" xml:space="preserve">
<value>The field {0} must be between {1} and {2} exclusive.</value>
</data>
<data name="RangeAttribute_ValidationError_MinExclusive_MaxExclusive" xml:space="preserve">
<value>The field {0} must be between {1} exclusive and {2} exclusive.</value>
</data>
<data name="RegexAttribute_ValidationError" xml:space="preserve">
<value>The field {0} must match the regular expression '{1}'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\ComponentModel\DataAnnotations\AllowedValuesAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\AssociatedMetadataTypeTypeDescriptor.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\AssociatedMetadataTypeTypeDescriptionProvider.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\AssociationAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\Base64StringAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\CompareAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\ConcurrencyCheckAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\CreditCardAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\CustomValidationAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DataType.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DataTypeAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DeniedValuesAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DisplayAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DisplayColumnAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\DisplayFormatAttribute.cs" />
Expand All @@ -27,6 +30,7 @@
<Compile Include="System\ComponentModel\DataAnnotations\FilterUIHintAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\IValidatableObject.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\KeyAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\LengthAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\LocalizableString.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\MaxLengthAttribute.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\MetadataPropertyDescriptorWrapper.cs" />
Expand Down Expand Up @@ -55,8 +59,7 @@
<Compile Include="System\ComponentModel\DataAnnotations\ValidationException.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\ValidationResult.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\Validator.cs" />
<Compile Include="$(CommonPath)System\NotImplemented.cs"
Link="Common\System\NotImplemented.cs" />
<Compile Include="$(CommonPath)System\NotImplemented.cs" Link="Common\System\NotImplemented.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.ComponentModel.DataAnnotations
{
/// <summary>
/// Specifies a list of values that should be allowed in a property.
/// </summary>
[CLSCompliant(false)]
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false)]
public class AllowedValuesAttribute : ValidationAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="AllowedValuesAttribute"/> class.
/// </summary>
/// <param name="values">
/// A list of values that the validated value should be equal to.
/// </param>
public AllowedValuesAttribute(params object?[] values)
{
ArgumentNullException.ThrowIfNull(values);
Values = values;
DefaultErrorMessage = SR.AllowedValuesAttribute_Invalid;
}

/// <summary>
/// Gets the list of values allowed by this attribute.
/// </summary>
public object?[] Values { get; }

/// <summary>
/// Determines whether a specified object is valid. (Overrides <see cref="ValidationAttribute.IsValid(object)" />)
/// </summary>
/// <param name="value">The object to validate.</param>
/// <returns>
/// <see langword="true" /> if any of the <see cref="Values"/> are equal to <paramref name="value"/>,
/// otherwise <see langword="false" />
/// </returns>
/// <remarks>
/// This method can return <see langword="true"/> if the <paramref name="value" /> is <see langword="null"/>,
/// provided that <see langword="null"/> is also specified in one of the <see cref="Values"/>.
/// </remarks>
public override bool IsValid(object? value)
{
foreach (object? allowed in Values)
{
if (allowed is null ? value is null : allowed.Equals(value))
{
return true;
}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;

namespace System.ComponentModel.DataAnnotations
{
/// <summary>
/// Specifies that a data field value is a well-formed Base64 string.
/// </summary>
/// <remarks>
/// Recognition of valid Base64 is delegated to the <see cref="Convert"/> class,
/// using the <see cref="Convert.TryFromBase64String(string, Span{byte}, out int)"/> method.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class Base64StringAttribute : ValidationAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="Base64StringAttribute"/> class.
/// </summary>
public Base64StringAttribute()
{
// Set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = SR.Base64StringAttribute_Invalid;
}

/// <summary>
/// Determines whether a specified object is valid. (Overrides <see cref="ValidationAttribute.IsValid(object)" />)
/// </summary>
/// <param name="value">The object to validate.</param>
/// <returns>
/// <see langword="true" /> if <paramref name="value"/> is <see langword="null"/> or is a valid Base64 string,
/// otherwise <see langword="false" />
/// </returns>
public override bool IsValid(object? value)
{
if (value is null)
{
return true;
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
}

if (value is not string valueAsString)
{
return false;
}

byte[]? rentedBuffer = null;
Span<byte> destinationBuffer = valueAsString.Length < 256
? stackalloc byte[256]
: rentedBuffer = ArrayPool<byte>.Shared.Rent(valueAsString.Length);

bool result = Convert.TryFromBase64String(valueAsString, destinationBuffer, out int bytesWritten);

if (rentedBuffer != null)
{
destinationBuffer.Slice(0, bytesWritten).Clear();
ArrayPool<byte>.Shared.Return(rentedBuffer);
}

return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.ComponentModel.DataAnnotations
{
/// <summary>
/// Specifies a list of values that should not be allowed in a property.
/// </summary>
[CLSCompliant(false)]
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false)]
public class DeniedValuesAttribute : ValidationAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DeniedValuesAttribute"/> class.
/// </summary>
/// <param name="values">
/// A list of values that the validated value should not be equal to.
/// </param>
public DeniedValuesAttribute(params object?[] values)
{
ArgumentNullException.ThrowIfNull(values);
Values = values;
DefaultErrorMessage = SR.DeniedValuesAttribute_Invalid;
}

/// <summary>
/// Gets the list of values denied by this attribute.
/// </summary>
public object?[] Values { get; }

/// <summary>
/// Determines whether a specified object is valid. (Overrides <see cref="ValidationAttribute.IsValid(object)" />)
/// </summary>
/// <param name="value">The object to validate.</param>
/// <returns>
/// <see langword="true" /> if none of the <see cref="Values"/> are equal to <paramref name="value"/>,
/// otherwise <see langword="false" />.
/// </returns>
/// <remarks>
/// This method can return <see langword="true"/> if the <paramref name="value" /> is <see langword="null"/>,
/// provided that <see langword="null"/> is not specified in any of the <see cref="Values"/>.
/// </remarks>
public override bool IsValid(object? value)
{
foreach (object? allowed in Values)
{
if (allowed is null ? value is null : allowed.Equals(value))
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Shouldn't the variable name be disallowed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point. Would you be interested in contributing a fix?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How? I'm newbie here. Don't know the best approach. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the change is fairly simple, you might be able to send a PR directly from the browser. Open up

https://github.com/dotnet/runtime/blob/main/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DeniedValuesAttribute.cs

And hit the edit button:

image

Make the change and follow the instructions to submit a PR. Done :-)

return false;
}
}

return true;
}
}
}
Loading