| __`SYSLIB1214`__ | Options validation generator: Can't validate constants, static fields or properties. |
| __`SYSLIB1215`__ | Options validation generator: Validation attribute on the member is inaccessible from the validator type. |
| __`SYSLIB1216`__ | C# language version not supported by the options validation source generator. |
-| __`SYSLIB1217`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
+| __`SYSLIB1217`__ | The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types. |
| __`SYSLIB1218`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1219`__ | *_`SYSLIB1201`-`SYSLIB1219` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1220`__ | JsonSourceGenerator encountered a [JsonConverterAttribute] with an invalid type argument. |
messageFormat: SR.OptionsUnsupportedLanguageVersionMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Error);
+
+ public static DiagnosticDescriptor IncompatibleWithTypeForValidationAttribute { get; } = Make(
+ id: "SYSLIB1217",
+ title: SR.TypeCannotBeUsedWithTheValidationAttributeTitle,
+ messageFormat: SR.TypeCannotBeUsedWithTheValidationAttributeMessage,
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Warning);
}
}
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
+using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
internal sealed class Emitter : EmitterBase
{
private const string StaticFieldHolderClassesNamespace = "__OptionValidationStaticInstances";
+ internal const string StaticGeneratedValidationAttributesClassesNamespace = "__OptionValidationGeneratedAttributes";
+ internal const string StaticAttributeClassNamePrefix = "__SourceGen_";
+ internal const string StaticGeneratedMaxLengthAttributeClassesName = "__SourceGen_MaxLengthAttribute";
private const string StaticListType = "global::System.Collections.Generic.List";
private const string StaticValidationResultType = "global::System.ComponentModel.DataAnnotations.ValidationResult";
private const string StaticValidationAttributeType = "global::System.ComponentModel.DataAnnotations.ValidationAttribute";
-
+ private const string StaticValidationContextType = "global::System.ComponentModel.DataAnnotations.ValidationContext";
private string _staticValidationAttributeHolderClassName = "__Attributes";
private string _staticValidatorHolderClassName = "__Validators";
private string _staticValidationAttributeHolderClassFQN;
private string _staticValidatorHolderClassFQN;
- private string _modifier;
private string _TryGetValueNullableAnnotation;
+ private readonly SymbolHolder _symbolHolder;
+ private readonly OptionsSourceGenContext _optionsSourceGenContext;
+
private sealed record StaticFieldInfo(string FieldTypeFQN, int FieldOrder, string FieldName, IList<string> InstantiationLines);
- public Emitter(Compilation compilation, bool emitPreamble = true) : base(emitPreamble)
+ public Emitter(Compilation compilation, SymbolHolder symbolHolder, OptionsSourceGenContext optionsSourceGenContext, bool emitPreamble = true) : base(emitPreamble)
{
- if (((CSharpCompilation)compilation).LanguageVersion >= Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp11)
- {
- _modifier = "file";
- }
- else
+ _optionsSourceGenContext = optionsSourceGenContext;
+
+ if (!_optionsSourceGenContext.IsLangVersion11AndAbove)
{
- _modifier = "internal";
- string suffix = $"_{GetNonRandomizedHashCode(compilation.SourceModule.Name):X8}";
- _staticValidationAttributeHolderClassName += suffix;
- _staticValidatorHolderClassName += suffix;
+ _staticValidationAttributeHolderClassName += _optionsSourceGenContext.Suffix;
+ _staticValidatorHolderClassName += _optionsSourceGenContext.Suffix;
}
_staticValidationAttributeHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{_staticValidationAttributeHolderClassName}";
_staticValidatorHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{_staticValidatorHolderClassName}";
_TryGetValueNullableAnnotation = GetNullableAnnotationStringForTryValidateValueToUseInGeneratedCode(compilation);
+
+ _symbolHolder = symbolHolder;
}
public string Emit(
GenStaticClassWithStaticReadonlyFields(staticValidationAttributesDict.Values, StaticFieldHolderClassesNamespace, _staticValidationAttributeHolderClassName);
GenStaticClassWithStaticReadonlyFields(staticValidatorsDict.Values, StaticFieldHolderClassesNamespace, _staticValidatorHolderClassName);
+ GenValidationAttributesClasses();
return Capture();
}
OutOpenBrace();
OutGeneratedCodeAttribute();
- OutLn($"{_modifier} static class {className}");
+ OutLn($"{_optionsSourceGenContext.ClassModifier} static class {className}");
OutOpenBrace();
var staticValidationAttributes = staticFields
OutCloseBrace();
}
+ public void EmitMaxLengthAttribute(string modifier, string prefix, string className, string linesToInsert, string suffix)
+ {
+ OutGeneratedCodeAttribute();
+
+ string qualifiedClassName = $"{prefix}{suffix}_{className}";
+
+ OutLn($$"""
+[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ {{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
+ {
+ private const int MaxAllowableLength = -1;
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a maximum length of '{1}'.";
+ public {{qualifiedClassName}}(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public {{qualifiedClassName}}(): base(() => DefaultErrorMessageString) { Length = MaxAllowableLength; }
+ public int Length { get; }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ public override bool IsValid(object? value)
+ {
+ if (Length == 0 || Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.");
+ }
+ if (value == null || MaxAllowableLength == Length)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ {{linesToInsert}}else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length <= Length;
+ }
+ }
+""");
+ }
+
+ public void EmitMinLengthAttribute(string modifier, string prefix, string className, string linesToInsert, string suffix)
+ {
+ OutGeneratedCodeAttribute();
+
+ string qualifiedClassName = $"{prefix}{suffix}_{className}";
+
+ OutLn($$"""
+[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ {{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public {{qualifiedClassName}}(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ {{linesToInsert}}else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+""");
+ }
+
+ public void EmitLengthAttribute(string modifier, string prefix, string className, string linesToInsert, string suffix)
+ {
+ OutGeneratedCodeAttribute();
+
+ string qualifiedClassName = $"{prefix}{suffix}_{className}";
+
+ OutLn($$"""
+[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ {{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or collection type with a minimum length of '{1}' and maximum length of '{2}'.";
+ public {{qualifiedClassName}}(int minimumLength, int maximumLength) : base(() => DefaultErrorMessageString) { MinimumLength = minimumLength; MaximumLength = maximumLength; }
+ public int MinimumLength { get; }
+ public int MaximumLength { get; }
+ public override bool IsValid(object? value)
+ {
+ if (MinimumLength < 0)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MinimumLength value that is zero or greater.");
+ }
+ if (MaximumLength < MinimumLength)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MaximumLength value that is greater than or equal to MinimumLength.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ {{linesToInsert}}else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return (uint)(length - MinimumLength) <= (uint)(MaximumLength - MinimumLength);
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, MinimumLength, MaximumLength);
+ }
+""");
+ }
+
+ public void EmitCompareAttribute(string modifier, string prefix, string className, string linesToInsert, string suffix)
+ {
+ OutGeneratedCodeAttribute();
+
+ string qualifiedClassName = $"{prefix}{suffix}_{className}";
+
+ OutLn($$"""
+[global::System.AttributeUsage(global::System.AttributeTargets.Property, AllowMultiple = false)]
+ {{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
+ {
+ private static string DefaultErrorMessageString => "'{0}' and '{1}' do not match.";
+ public {{qualifiedClassName}}(string otherProperty) : base(() => DefaultErrorMessageString)
+ {
+ if (otherProperty == null)
+ {
+ throw new global::System.ArgumentNullException(nameof(otherProperty));
+ }
+ OtherProperty = otherProperty;
+ }
+ public string OtherProperty { get; }
+ public override bool RequiresValidationContext => true;
+
+ protected override {{StaticValidationResultType}}? IsValid(object? value, {{StaticValidationContextType}} validationContext)
+ {
+ bool result = true;
+
+ {{linesToInsert}}
+ if (!result)
+ {
+ string[]? memberNames = validationContext.MemberName is null ? null : new string[] { validationContext.MemberName };
+ return new {{StaticValidationResultType}}(FormatErrorMessage(validationContext.DisplayName), memberNames);
+ }
+
+ return null;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, OtherProperty);
+ }
+""");
+ }
+
+ public void EmitRangeAttribute(string modifier, string prefix, string className, string suffix)
+ {
+ OutGeneratedCodeAttribute();
+
+ string qualifiedClassName = $"{prefix}{suffix}_{className}";
+
+ OutLn($$"""
+[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ {{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
+ {
+ public {{qualifiedClassName}}(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public {{qualifiedClassName}}(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public {{qualifiedClassName}}(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+""");
+ }
+
+ private string GenerateStronglyTypedCodeForLengthAttributes(HashSet<object> data)
+ {
+ if (data.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder sb = new();
+ string padding = GetPaddingString(3);
+
+ foreach (var type in data)
+ {
+ string typeName = (string)type;
+ sb.AppendLine($"else if (value is {typeName})");
+ sb.AppendLine($"{padding}{{");
+ sb.AppendLine($"{padding} length = (({typeName})value).Count;");
+ sb.AppendLine($"{padding}}}");
+ sb.Append($"{padding}");
+ }
+
+ return sb.ToString();
+ }
+
+ private string GenerateStronglyTypedCodeForCompareAttribute(HashSet<object>? data)
+ {
+ if (data is null || data.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder sb = new();
+ string padding = GetPaddingString(3);
+ bool first = true;
+
+ foreach (var obj in data)
+ {
+ (string type, string property) = ((string, string))obj;
+ sb.Append(first ? $"if " : $"{padding}else if ");
+ sb.AppendLine($"(validationContext.ObjectInstance is {type} && OtherProperty == \"{property}\")");
+ sb.AppendLine($"{padding}{{");
+ sb.AppendLine($"{padding} result = Equals(value, (({type})validationContext.ObjectInstance).{property});");
+ sb.AppendLine($"{padding}}}");
+ first = false;
+ }
+
+ return sb.ToString();
+ }
+
+ private void GenValidationAttributesClasses()
+ {
+ if (_optionsSourceGenContext.AttributesToGenerate.Count == 0)
+ {
+ return;
+ }
+
+ var attributesData = _optionsSourceGenContext.AttributesToGenerate.OrderBy(static kvp => kvp.Key, StringComparer.Ordinal).ToArray();
+
+ OutLn($"namespace {StaticGeneratedValidationAttributesClassesNamespace}");
+ OutOpenBrace();
+
+ foreach (var attributeData in attributesData)
+ {
+ if (attributeData.Key == _symbolHolder.MaxLengthAttributeSymbol.Name)
+ {
+ string linesToInsert = attributeData.Value is not null ? GenerateStronglyTypedCodeForLengthAttributes((HashSet<object>)attributeData.Value) : string.Empty;
+ EmitMaxLengthAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, linesToInsert, _optionsSourceGenContext.Suffix);
+ }
+ else if (attributeData.Key == _symbolHolder.MinLengthAttributeSymbol.Name)
+ {
+ string linesToInsert = attributeData.Value is not null ? GenerateStronglyTypedCodeForLengthAttributes((HashSet<object>)attributeData.Value) : string.Empty;
+ EmitMinLengthAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, linesToInsert, _optionsSourceGenContext.Suffix);
+ }
+ else if (_symbolHolder.LengthAttributeSymbol is not null && attributeData.Key == _symbolHolder.LengthAttributeSymbol.Name)
+ {
+ string linesToInsert = attributeData.Value is not null ? GenerateStronglyTypedCodeForLengthAttributes((HashSet<object>)attributeData.Value) : string.Empty;
+ EmitLengthAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, linesToInsert, _optionsSourceGenContext.Suffix);
+ }
+ else if (attributeData.Key == _symbolHolder.CompareAttributeSymbol.Name && attributeData.Value is not null)
+ {
+ string linesToInsert = GenerateStronglyTypedCodeForCompareAttribute((HashSet<object>)attributeData.Value);
+ EmitCompareAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, linesToInsert: linesToInsert, _optionsSourceGenContext.Suffix);
+ }
+ else if (attributeData.Key == _symbolHolder.RangeAttributeSymbol.Name)
+ {
+ EmitRangeAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, _optionsSourceGenContext.Suffix);
+ }
+ }
+
+ OutCloseBrace();
+ }
+
private void GenModelSelfValidationIfNecessary(ValidatedModel modelToValidate)
{
if (modelToValidate.SelfValidates)
OutLn($"/// <returns>Validation result.</returns>");
OutGeneratedCodeAttribute();
+ if (_symbolHolder.UnconditionalSuppressMessageAttributeSymbol is not null)
+ {
+ // We disable the warning on `new ValidationContext(object)` usage as we use it in a safe way that not require executing the reflection code.
+ // This is done by initializing the DisplayName in the context which is the part trigger reflection if it is not initialized.
+ OutLn($"[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage(\"Trimming\", \"IL2026:RequiresUnreferencedCode\",");
+ OutLn($" Justification = \"The created ValidationContext object is used in a way that never call reflection\")]");
+ }
+
OutLn($"public {(makeStatic ? "static " : string.Empty)}global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, {modelToValidate.Name} options)");
OutOpenBrace();
OutLn($"global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;");
- OutLn($"var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);");
+ OutLn($"var context = new {StaticValidationContextType}(options);");
int capacity = modelToValidate.MembersToValidate.Max(static vm => vm.ValidationAttributes.Count);
if (capacity > 0)
return staticValidatorInstance;
}
-
- /// <summary>
- /// Returns a non-randomized hash code for the given string.
- /// We always return a positive value.
- /// </summary>
- internal static int GetNonRandomizedHashCode(string s)
- {
- uint result = 2166136261u;
- foreach (char c in s)
- {
- result = (c ^ result) * 16777619;
- }
- return Math.Abs((int)result);
- }
}
}
return;
}
- var parser = new Parser(compilation, context.ReportDiagnostic, symbolHolder!, context.CancellationToken);
+ OptionsSourceGenContext optionsSourceGenContext = new(compilation);
+
+ var parser = new Parser(compilation, context.ReportDiagnostic, symbolHolder!, optionsSourceGenContext, context.CancellationToken);
var validatorTypes = parser.GetValidatorTypes(types);
if (validatorTypes.Count > 0)
{
- var emitter = new Emitter(compilation);
+ var emitter = new Emitter(compilation, symbolHolder!, optionsSourceGenContext);
var result = emitter.Emit(validatorTypes, context.CancellationToken);
context.AddSource("Validators.g.cs", SourceText.From(result, Encoding.UTF8));
<Compile Include="Model\ValidatedModel.cs" />
<Compile Include="Model\ValidationAttributeInfo.cs" />
<Compile Include="Model\ValidatorType.cs" />
+ <Compile Include="OptionsSourceGenContext.cs" />
<Compile Include="Parser.cs" />
<Compile Include="ParserUtilities.cs" />
<Compile Include="SymbolHolder.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Versioning;
+
+namespace Microsoft.Extensions.Options.Generators
+{
+ internal sealed class OptionsSourceGenContext
+ {
+ public OptionsSourceGenContext(Compilation compilation)
+ {
+ IsLangVersion11AndAbove = ((CSharpCompilation)compilation).LanguageVersion >= Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp11;
+ ClassModifier = IsLangVersion11AndAbove ? "file" : "internal";
+ Suffix = IsLangVersion11AndAbove ? "" : $"_{GetNonRandomizedHashCode(compilation.SourceModule.Name):X8}";
+ }
+
+ internal string Suffix { get; }
+ internal string ClassModifier { get; }
+ internal bool IsLangVersion11AndAbove { get; }
+ internal Dictionary<string, HashSet<object>?> AttributesToGenerate { get; set; } = new Dictionary<string, HashSet<object>?>();
+
+ internal void EnsureTrackingAttribute(string attributeName, bool createValue, out HashSet<object>? value)
+ {
+ bool exist = AttributesToGenerate.TryGetValue(attributeName, out value);
+ if (value is null)
+ {
+ if (createValue)
+ {
+ value = new HashSet<object>();
+ }
+
+ if (!exist || createValue)
+ {
+ AttributesToGenerate[attributeName] = value;
+ }
+ }
+ }
+
+ internal static bool IsConvertibleBasicType(ITypeSymbol typeSymbol)
+ {
+ return typeSymbol.SpecialType switch
+ {
+ SpecialType.System_Boolean => true,
+ SpecialType.System_Byte => true,
+ SpecialType.System_Char => true,
+ SpecialType.System_DateTime => true,
+ SpecialType.System_Decimal => true,
+ SpecialType.System_Double => true,
+ SpecialType.System_Int16 => true,
+ SpecialType.System_Int32 => true,
+ SpecialType.System_Int64 => true,
+ SpecialType.System_SByte => true,
+ SpecialType.System_Single => true,
+ SpecialType.System_UInt16 => true,
+ SpecialType.System_UInt32 => true,
+ SpecialType.System_UInt64 => true,
+ SpecialType.System_String => true,
+ _ => false,
+ };
+ }
+
+ /// <summary>
+ /// Returns a non-randomized hash code for the given string.
+ /// We always return a positive value.
+ /// </summary>
+ internal static int GetNonRandomizedHashCode(string s)
+ {
+ uint result = 2166136261u;
+ foreach (char c in s)
+ {
+ result = (c ^ result) * 16777619;
+ }
+
+ return Math.Abs((int)result);
+ }
+ }
+}
private readonly Compilation _compilation;
private readonly Action<Diagnostic> _reportDiagnostic;
private readonly SymbolHolder _symbolHolder;
+ private readonly OptionsSourceGenContext _optionsSourceGenContext;
private readonly Dictionary<ITypeSymbol, ValidatorType> _synthesizedValidators = new(SymbolEqualityComparer.Default);
private readonly HashSet<ITypeSymbol> _visitedModelTypes = new(SymbolEqualityComparer.Default);
Compilation compilation,
Action<Diagnostic> reportDiagnostic,
SymbolHolder symbolHolder,
+ OptionsSourceGenContext optionsSourceGenContext,
CancellationToken cancellationToken)
{
_compilation = compilation;
_cancellationToken = cancellationToken;
_reportDiagnostic = reportDiagnostic;
_symbolHolder = symbolHolder;
+ _optionsSourceGenContext = optionsSourceGenContext;
}
public IReadOnlyList<ValidatorType> GetValidatorTypes(IEnumerable<(TypeDeclarationSyntax TypeSyntax, SemanticModel SemanticModel)> classes)
? memberLocation
: lowerLocationInCompilation;
- var memberInfo = GetMemberInfo(member, speculate, location, validatorType);
+ var memberInfo = GetMemberInfo(member, speculate, location, modelType, validatorType);
if (memberInfo is not null)
{
if (member.DeclaredAccessibility != Accessibility.Public)
return membersToValidate;
}
- private ValidatedMember? GetMemberInfo(ISymbol member, bool speculate, Location location, ITypeSymbol validatorType)
+ private ValidatedMember? GetMemberInfo(ISymbol member, bool speculate, Location location, ITypeSymbol modelType, ITypeSymbol validatorType)
{
ITypeSymbol memberType;
switch (member)
break;
*/
default:
- // we only care about properties and fields
+ // we only care about properties
return null;
}
continue;
}
- var validationAttr = new ValidationAttributeInfo(attributeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ string attributeFullQualifiedName = attributeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ if (SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.MaxLengthAttributeSymbol) ||
+ SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.MinLengthAttributeSymbol) ||
+ (_symbolHolder.LengthAttributeSymbol is not null && SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.LengthAttributeSymbol)))
+ {
+ if (!LengthBasedAttributeIsTrackedForSubstitution(memberType, location, attributeType, ref attributeFullQualifiedName))
+ {
+ continue;
+ }
+ }
+ else if (SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.CompareAttributeSymbol))
+ {
+ TrackCompareAttributeForSubstitution(attribute, modelType, ref attributeFullQualifiedName);
+ }
+ else if (SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.RangeAttributeSymbol))
+ {
+ TrackRangeAttributeForSubstitution(attribute, memberType, ref attributeFullQualifiedName);
+ }
+
+ var validationAttr = new ValidationAttributeInfo(attributeFullQualifiedName);
validationAttrs.Add(validationAttr);
ImmutableArray<IParameterSymbol> parameters = attribute.AttributeConstructor?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty;
return null;
}
+ private bool LengthBasedAttributeIsTrackedForSubstitution(ITypeSymbol memberType, Location location, ITypeSymbol attributeType, ref string attributeFullQualifiedName)
+ {
+ if (memberType.SpecialType == SpecialType.System_String || ConvertTo(memberType, _symbolHolder.ICollectionSymbol))
+ {
+ _optionsSourceGenContext.EnsureTrackingAttribute(attributeType.Name, createValue: false, out _);
+ }
+ else if (ParserUtilities.TypeHasProperty(memberType, "Count", SpecialType.System_Int32))
+ {
+ _optionsSourceGenContext.EnsureTrackingAttribute(attributeType.Name, createValue: true, out HashSet<object>? trackedTypeList);
+ trackedTypeList!.Add(memberType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+ else
+ {
+ Diag(DiagDescriptors.IncompatibleWithTypeForValidationAttribute, location, attributeType.Name, memberType.Name);
+ return false;
+ }
+
+ attributeFullQualifiedName = $"{Emitter.StaticGeneratedValidationAttributesClassesNamespace}.{Emitter.StaticAttributeClassNamePrefix}{_optionsSourceGenContext.Suffix}_{attributeType.Name}";
+ return true;
+ }
+
+ private void TrackCompareAttributeForSubstitution(AttributeData attribute, ITypeSymbol modelType, ref string attributeFullQualifiedName)
+ {
+ ImmutableArray<IParameterSymbol> constructorParameters = attribute.AttributeConstructor?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty;
+ if (constructorParameters.Length == 1 && constructorParameters[0].Name == "otherProperty" && constructorParameters[0].Type.SpecialType == SpecialType.System_String)
+ {
+ _optionsSourceGenContext.EnsureTrackingAttribute(attribute.AttributeClass!.Name, createValue: true, out HashSet<object>? trackedTypeList);
+ trackedTypeList!.Add((modelType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), (string)attribute.ConstructorArguments[0].Value!));
+ attributeFullQualifiedName = $"{Emitter.StaticGeneratedValidationAttributesClassesNamespace}.{Emitter.StaticAttributeClassNamePrefix}{_optionsSourceGenContext.Suffix}_{attribute.AttributeClass!.Name}";
+ }
+ }
+
+ private void TrackRangeAttributeForSubstitution(AttributeData attribute, ITypeSymbol memberType, ref string attributeFullQualifiedName)
+ {
+ ImmutableArray<IParameterSymbol> constructorParameters = attribute.AttributeConstructor?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty;
+ SpecialType argumentSpecialType = SpecialType.None;
+ if (constructorParameters.Length == 2)
+ {
+ argumentSpecialType = constructorParameters[0].Type.SpecialType;
+ }
+ else if (constructorParameters.Length == 3)
+ {
+ object? argumentValue = null;
+ for (int i = 0; i < constructorParameters.Length; i++)
+ {
+ if (constructorParameters[i].Name == "type")
+ {
+ argumentValue = attribute.ConstructorArguments[i].Value;
+ break;
+ }
+ }
+
+ if (argumentValue is INamedTypeSymbol namedTypeSymbol && OptionsSourceGenContext.IsConvertibleBasicType(namedTypeSymbol))
+ {
+ argumentSpecialType = namedTypeSymbol.SpecialType;
+ }
+ }
+
+ ITypeSymbol typeSymbol = memberType;
+ if (typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
+ {
+ typeSymbol = ((INamedTypeSymbol)typeSymbol).TypeArguments[0];
+ }
+
+ if (argumentSpecialType != SpecialType.None &&
+ OptionsSourceGenContext.IsConvertibleBasicType(typeSymbol) &&
+ (constructorParameters.Length != 3 || typeSymbol.SpecialType == argumentSpecialType)) // When type is provided as a parameter, it has to match the property type.
+ {
+ _optionsSourceGenContext.EnsureTrackingAttribute(attribute.AttributeClass!.Name, createValue: false, out _);
+ attributeFullQualifiedName = $"{Emitter.StaticGeneratedValidationAttributesClassesNamespace}.{Emitter.StaticAttributeClassNamePrefix}{_optionsSourceGenContext.Suffix}_{attribute.AttributeClass!.Name}";
+ }
+ }
+
private string? AddSynthesizedValidator(ITypeSymbol modelType, ISymbol member, Location location, ITypeSymbol validatorType)
{
var mt = modelType.WithNullableAnnotation(NullableAnnotation.None);
return false;
}
+ internal static bool TypeHasProperty(ITypeSymbol typeSymbol, string propertyName, SpecialType returnType)
+ {
+ ITypeSymbol? type = typeSymbol;
+ do
+ {
+ if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
+ {
+ type = ((INamedTypeSymbol)type).TypeArguments[0]; // extract the T from a Nullable<T>
+ }
+
+ if (type.GetMembers(propertyName).OfType<IPropertySymbol>().Any(property =>
+ property.Type.SpecialType == returnType && property.DeclaredAccessibility == Accessibility.Public &&
+ !property.IsStatic && property.GetMethod != null && property.Parameters.IsEmpty))
+ {
+ return true;
+ }
+
+ type = type.BaseType;
+ } while (type is not null && type.SpecialType != SpecialType.System_Object);
+
+ return false;
+ }
+
// Check if parameter has either simplified (i.e. "int?") or explicit (Nullable<int>) nullable type declaration:
internal static bool IsNullableOfT(this ITypeSymbol type)
=> type.SpecialType == SpecialType.System_Nullable_T || type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
<data name="OptionsUnsupportedLanguageVersionMessage" xml:space="preserve">
<value>The options validation source generator is not available in C# {0}. Please use language version {1} or greater.</value>
</data>
+ <data name="TypeCannotBeUsedWithTheValidationAttributeTitle" xml:space="preserve">
+ <value>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</value>
+ </data>
+ <data name="TypeCannotBeUsedWithTheValidationAttributeMessage" xml:space="preserve">
+ <value>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</value>
+ </data>
</root>
<target state="translated">U člena potenciálně chybí přenositelné ověření.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Typ validátoru {0} nemá konstruktor bez parametrů.</target>
<target state="translated">Dem Member fehlt möglicherweise die transitive Validierung.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Der Validierungssteuerelementtyp "{0}" hat keinen parameterlosen Konstruktor.</target>
<target state="translated">Posiblemente falta la validación transitiva en el miembro.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">El tipo de validador {0} no tiene un constructor sin parámetros.</target>
<target state="translated">Le membre n’a peut-être pas de validation transitive.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Le type de validateur {0} n’a pas de constructeur sans paramètre.</target>
<target state="translated">Il membro potrebbe non avere una convalida transitiva.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Il tipo di convalida {0} non dispone di un costruttore senza parametri.</target>
<target state="translated">メンバーに推移性の検証がない可能性があります。</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">バリデーター型 {0} にパラメーターなしのコンストラクターがありません。</target>
<target state="translated">멤버에 전이적 유효성 검사가 누락되었을 수 있습니다.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">유효성 검사기 형식 {0}은(는) 매개 변수가 없는 생성자가 없습니다.</target>
<target state="translated">W przypadku elementu członkowskiego może potencjalnie brakować weryfikacji przechodniej.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Typ modułu sprawdzania poprawności {0} nie ma konstruktora bez parametrów.</target>
<target state="translated">Membro potencialmente ausente na validação transitiva.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">O tipo de validador {0} não tem um construtor sem parâmetros.</target>
<target state="translated">Возможно, в элементе отсутствует транзитивная проверка.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">Тип проверяющего элемента управления {0} не имеет конструктора без параметров.</target>
<target state="translated">Üyede geçişli doğrulama eksik olabilir.</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">{0} doğrulayıcı türü parametresiz bir oluşturucuya sahip değil.</target>
<target state="translated">成员可能缺少可传递验证。</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">验证程序类型 {0} 没有无参数构造函数。</target>
<target state="translated">成員可能遺漏轉移的驗證。</target>
<note />
</trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeMessage">
+ <source>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</source>
+ <target state="new">The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="TypeCannotBeUsedWithTheValidationAttributeTitle">
+ <source>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</source>
+ <target state="new">The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="translated">驗證程式類型 {0} 沒有無參數建構函式。</target>
internal sealed record class SymbolHolder(
INamedTypeSymbol OptionsValidatorSymbol,
INamedTypeSymbol ValidationAttributeSymbol,
+ INamedTypeSymbol MaxLengthAttributeSymbol,
+ INamedTypeSymbol MinLengthAttributeSymbol,
+ INamedTypeSymbol CompareAttributeSymbol,
+ INamedTypeSymbol? LengthAttributeSymbol,
+ INamedTypeSymbol? UnconditionalSuppressMessageAttributeSymbol,
+ INamedTypeSymbol RangeAttributeSymbol,
+ INamedTypeSymbol ICollectionSymbol,
INamedTypeSymbol DataTypeAttributeSymbol,
INamedTypeSymbol ValidateOptionsSymbol,
INamedTypeSymbol IValidatableObjectSymbol,
{
public const string OptionsValidatorAttribute = "Microsoft.Extensions.Options.OptionsValidatorAttribute";
internal const string ValidationAttribute = "System.ComponentModel.DataAnnotations.ValidationAttribute";
+ internal const string MaxLengthAttribute = "System.ComponentModel.DataAnnotations.MaxLengthAttribute";
+ internal const string MinLengthAttribute = "System.ComponentModel.DataAnnotations.MinLengthAttribute";
+ internal const string CompareAttribute = "System.ComponentModel.DataAnnotations.CompareAttribute";
+ internal const string LengthAttribute = "System.ComponentModel.DataAnnotations.LengthAttribute";
+ internal const string RangeAttribute = "System.ComponentModel.DataAnnotations.RangeAttribute";
+ internal const string ICollectionType = "System.Collections.ICollection";
internal const string DataTypeAttribute = "System.ComponentModel.DataAnnotations.DataTypeAttribute";
internal const string IValidatableObjectType = "System.ComponentModel.DataAnnotations.IValidatableObject";
internal const string IValidateOptionsType = "Microsoft.Extensions.Options.IValidateOptions`1";
internal const string ValidateObjectMembersAttribute = "Microsoft.Extensions.Options.ValidateObjectMembersAttribute";
internal const string ValidateEnumeratedItemsAttribute = "Microsoft.Extensions.Options.ValidateEnumeratedItemsAttribute";
internal const string GenericIEnumerableType = "System.Collections.Generic.IEnumerable`1";
+ internal const string UnconditionalSuppressMessageAttributeType = "System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute";
public static bool TryLoad(Compilation compilation, out SymbolHolder? symbolHolder)
{
// required
var optionsValidatorSymbol = GetSymbol(OptionsValidatorAttribute);
var validationAttributeSymbol = GetSymbol(ValidationAttribute);
+ var maxLengthAttributeSymbol = GetSymbol(MaxLengthAttribute);
+ var minLengthAttributeSymbol = GetSymbol(MinLengthAttribute);
+ var compareAttributeSymbol = GetSymbol(CompareAttribute);
+ var lengthAttributeSymbol = GetSymbol(LengthAttribute);
+ var rangeAttributeSymbol = GetSymbol(RangeAttribute);
+ var iCollectionSymbol = GetSymbol(ICollectionType);
var dataTypeAttributeSymbol = GetSymbol(DataTypeAttribute);
var ivalidatableObjectSymbol = GetSymbol(IValidatableObjectType);
var validateOptionsSymbol = GetSymbol(IValidateOptionsType);
var typeSymbol = GetSymbol(TypeOfType);
var validateObjectMembersAttribute = GetSymbol(ValidateObjectMembersAttribute);
var validateEnumeratedItemsAttribute = GetSymbol(ValidateEnumeratedItemsAttribute);
+ var unconditionalSuppressMessageAttributeSymbol = GetSymbol(UnconditionalSuppressMessageAttributeType);
+ if (unconditionalSuppressMessageAttributeSymbol is not null)
+ {
+ var containingAssemblyName = unconditionalSuppressMessageAttributeSymbol.ContainingAssembly.Identity.Name;
+ if (!containingAssemblyName.Equals("System.Private.CoreLib", System.StringComparison.OrdinalIgnoreCase) &&
+ !containingAssemblyName.Equals("System.Runtime", System.StringComparison.OrdinalIgnoreCase))
+ {
+ // The compilation returns UnconditionalSuppressMessageAttribute symbol even if the attribute is not available like the case when running on .NET Framework.
+ // We need to make sure that the attribute is really available by checking the containing assembly which in .NET Core will be either System.Private.CoreLib or System.Runtime.
+ unconditionalSuppressMessageAttributeSymbol = null;
+ }
+ }
#pragma warning disable S1067 // Expressions should not be too complex
if (optionsValidatorSymbol == null ||
validationAttributeSymbol == null ||
+ maxLengthAttributeSymbol == null ||
+ minLengthAttributeSymbol == null ||
+ compareAttributeSymbol == null ||
+ rangeAttributeSymbol == null ||
+ iCollectionSymbol == null ||
dataTypeAttributeSymbol == null ||
ivalidatableObjectSymbol == null ||
validateOptionsSymbol == null ||
symbolHolder = new(
optionsValidatorSymbol,
validationAttributeSymbol,
+ maxLengthAttributeSymbol,
+ minLengthAttributeSymbol,
+ compareAttributeSymbol,
+ lengthAttributeSymbol,
+ unconditionalSuppressMessageAttributeSymbol,
+ rangeAttributeSymbol,
+ iCollectionSymbol,
dataTypeAttributeSymbol,
validateOptionsSymbol,
ivalidatableObjectSymbol,
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<Reference Include="System.ComponentModel.DataAnnotations" />
- <PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
+ <PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\gen\Microsoft.Extensions.Options.SourceGeneration.csproj"
ReferenceOutputAssembly="false"
+ SetTargetFramework="TargetFramework=netstandard2.0"
PackAsAnalyzer="true" />
</ItemGroup>
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace Test
+{
+ partial class MyOptionsValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.MyOptions options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "P1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P1" : $"{name}.P1";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P2" : $"{name}.P2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P3" : $"{name}.P3";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Attributes
+ {
+ internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__LengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__LengthAttribute(
+ (int)10,
+ (int)20);
+
+ internal static readonly global::System.ComponentModel.DataAnnotations.AllowedValuesAttribute A3 = new global::System.ComponentModel.DataAnnotations.AllowedValuesAttribute(
+ (int)10, (int)20, (int)30);
+
+ internal static readonly global::System.ComponentModel.DataAnnotations.DeniedValuesAttribute A4 = new global::System.ComponentModel.DataAnnotations.DeniedValuesAttribute(
+ "One", "Ten", "Hundred");
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Validators
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__LengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or collection type with a minimum length of '{1}' and maximum length of '{2}'.";
+ public __SourceGen__LengthAttribute(int minimumLength, int maximumLength) : base(() => DefaultErrorMessageString) { MinimumLength = minimumLength; MaximumLength = maximumLength; }
+ public int MinimumLength { get; }
+ public int MaximumLength { get; }
+ public override bool IsValid(object? value)
+ {
+ if (MinimumLength < 0)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MinimumLength value that is zero or greater.");
+ }
+ if (MaximumLength < MinimumLength)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MaximumLength value that is greater than or equal to MinimumLength.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return (uint)(length - MinimumLength) <= (uint)(MaximumLength - MinimumLength);
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, MinimumLength, MaximumLength);
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace HelloWorld
+{
+ partial struct MyOptionsValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::HelloWorld.MyOptions options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "Val1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val1" : $"{name}.Val1";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "Val2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val2" : $"{name}.Val2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Attributes
+ {
+ internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ (int)1,
+ (int)3);
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Validators
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace HelloWorld
+{
+ partial struct MyOptionsValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::HelloWorld.MyOptions options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "Val1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val1" : $"{name}.Val1";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "Val2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val2" : $"{name}.Val2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Attributes
+ {
+ internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ (int)1,
+ (int)3);
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Validators
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace ValidationTest
+{
+ partial class OptionsUsingGeneratedAttributesValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "P0";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P0" : $"{name}.P0";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P0, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P1" : $"{name}.P1";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P2" : $"{name}.P2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P3" : $"{name}.P3";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P5";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P5" : $"{name}.P5";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P5, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P6";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P6" : $"{name}.P6";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A5);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P6, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P7";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P7" : $"{name}.P7";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P7, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P8";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P8" : $"{name}.P8";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P8, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P9";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P9" : $"{name}.P9";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P9, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P10";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P10" : $"{name}.P10";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P10, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P11";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P11" : $"{name}.P11";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P11, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P12";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P12" : $"{name}.P12";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P12, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ internal static class __Attributes_2C497155
+ {
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_LengthAttribute A1 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_LengthAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MinLengthAttribute A3 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MinLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MaxLengthAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MaxLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute(
+ "P5");
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ internal static class __Validators_2C497155
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_CompareAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "'{0}' and '{1}' do not match.";
+ public __SourceGen__2C497155_CompareAttribute(string otherProperty) : base(() => DefaultErrorMessageString)
+ {
+ if (otherProperty == null)
+ {
+ throw new global::System.ArgumentNullException(nameof(otherProperty));
+ }
+ OtherProperty = otherProperty;
+ }
+ public string OtherProperty { get; }
+ public override bool RequiresValidationContext => true;
+
+ protected override global::System.ComponentModel.DataAnnotations.ValidationResult? IsValid(object? value, global::System.ComponentModel.DataAnnotations.ValidationContext validationContext)
+ {
+ bool result = true;
+
+ if (validationContext.ObjectInstance is global::ValidationTest.OptionsUsingGeneratedAttributes && OtherProperty == "P5")
+ {
+ result = Equals(value, ((global::ValidationTest.OptionsUsingGeneratedAttributes)validationContext.ObjectInstance).P5);
+ }
+
+ if (!result)
+ {
+ string[]? memberNames = validationContext.MemberName is null ? null : new string[] { validationContext.MemberName };
+ return new global::System.ComponentModel.DataAnnotations.ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
+ }
+
+ return null;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, OtherProperty);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_LengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or collection type with a minimum length of '{1}' and maximum length of '{2}'.";
+ public __SourceGen__2C497155_LengthAttribute(int minimumLength, int maximumLength) : base(() => DefaultErrorMessageString) { MinimumLength = minimumLength; MaximumLength = maximumLength; }
+ public int MinimumLength { get; }
+ public int MaximumLength { get; }
+ public override bool IsValid(object? value)
+ {
+ if (MinimumLength < 0)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MinimumLength value that is zero or greater.");
+ }
+ if (MaximumLength < MinimumLength)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MaximumLength value that is greater than or equal to MinimumLength.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return (uint)(length - MinimumLength) <= (uint)(MaximumLength - MinimumLength);
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, MinimumLength, MaximumLength);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_MaxLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private const int MaxAllowableLength = -1;
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a maximum length of '{1}'.";
+ public __SourceGen__2C497155_MaxLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public __SourceGen__2C497155_MaxLengthAttribute(): base(() => DefaultErrorMessageString) { Length = MaxAllowableLength; }
+ public int Length { get; }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ public override bool IsValid(object? value)
+ {
+ if (Length == 0 || Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.");
+ }
+ if (value == null || MaxAllowableLength == Length)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length <= Length;
+ }
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__2C497155_MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__2C497155_RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace ValidationTest
+{
+ partial class OptionsUsingGeneratedAttributesValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "P0";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P0" : $"{name}.P0";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P0, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P1" : $"{name}.P1";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P2" : $"{name}.P2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P3" : $"{name}.P3";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P5";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P5" : $"{name}.P5";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P5, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P6";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P6" : $"{name}.P6";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A5);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P6, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P7";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P7" : $"{name}.P7";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P7, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P8";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P8" : $"{name}.P8";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P8, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P9";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P9" : $"{name}.P9";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P9, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P10";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P10" : $"{name}.P10";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P10, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P11";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P11" : $"{name}.P11";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P11, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P12";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P12" : $"{name}.P12";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P12, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Attributes
+ {
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__LengthAttribute A1 = new __OptionValidationGeneratedAttributes.__SourceGen__LengthAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute A3 = new __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MaxLengthAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__MaxLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute(
+ "P5");
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Validators
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property, AllowMultiple = false)]
+ file class __SourceGen__CompareAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "'{0}' and '{1}' do not match.";
+ public __SourceGen__CompareAttribute(string otherProperty) : base(() => DefaultErrorMessageString)
+ {
+ if (otherProperty == null)
+ {
+ throw new global::System.ArgumentNullException(nameof(otherProperty));
+ }
+ OtherProperty = otherProperty;
+ }
+ public string OtherProperty { get; }
+ public override bool RequiresValidationContext => true;
+
+ protected override global::System.ComponentModel.DataAnnotations.ValidationResult? IsValid(object? value, global::System.ComponentModel.DataAnnotations.ValidationContext validationContext)
+ {
+ bool result = true;
+
+ if (validationContext.ObjectInstance is global::ValidationTest.OptionsUsingGeneratedAttributes && OtherProperty == "P5")
+ {
+ result = Equals(value, ((global::ValidationTest.OptionsUsingGeneratedAttributes)validationContext.ObjectInstance).P5);
+ }
+
+ if (!result)
+ {
+ string[]? memberNames = validationContext.MemberName is null ? null : new string[] { validationContext.MemberName };
+ return new global::System.ComponentModel.DataAnnotations.ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
+ }
+
+ return null;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, OtherProperty);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__LengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or collection type with a minimum length of '{1}' and maximum length of '{2}'.";
+ public __SourceGen__LengthAttribute(int minimumLength, int maximumLength) : base(() => DefaultErrorMessageString) { MinimumLength = minimumLength; MaximumLength = maximumLength; }
+ public int MinimumLength { get; }
+ public int MaximumLength { get; }
+ public override bool IsValid(object? value)
+ {
+ if (MinimumLength < 0)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MinimumLength value that is zero or greater.");
+ }
+ if (MaximumLength < MinimumLength)
+ {
+ throw new global::System.InvalidOperationException("LengthAttribute must have a MaximumLength value that is greater than or equal to MinimumLength.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return (uint)(length - MinimumLength) <= (uint)(MaximumLength - MinimumLength);
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, MinimumLength, MaximumLength);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MaxLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private const int MaxAllowableLength = -1;
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a maximum length of '{1}'.";
+ public __SourceGen__MaxLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public __SourceGen__MaxLengthAttribute(): base(() => DefaultErrorMessageString) { Length = MaxAllowableLength; }
+ public int Length { get; }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ public override bool IsValid(object? value)
+ {
+ if (Length == 0 || Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.");
+ }
+ if (value == null || MaxAllowableLength == Length)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length <= Length;
+ }
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace ValidationTest
+{
+ partial class OptionsUsingGeneratedAttributesValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P3" : $"{name}.P3";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P5";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P5" : $"{name}.P5";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P5, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P6";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P6" : $"{name}.P6";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P6, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P7";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P7" : $"{name}.P7";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P7, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P8";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P8" : $"{name}.P8";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P8, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P9";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P9" : $"{name}.P9";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P9, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P10";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P10" : $"{name}.P10";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P10, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P11";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P11" : $"{name}.P11";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P11, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P12";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P12" : $"{name}.P12";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P12, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ internal static class __Attributes_2C497155
+ {
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A1 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MinLengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MinLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MaxLengthAttribute A3 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_MaxLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute(
+ "P5");
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ internal static class __Validators_2C497155
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_CompareAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "'{0}' and '{1}' do not match.";
+ public __SourceGen__2C497155_CompareAttribute(string otherProperty) : base(() => DefaultErrorMessageString)
+ {
+ if (otherProperty == null)
+ {
+ throw new global::System.ArgumentNullException(nameof(otherProperty));
+ }
+ OtherProperty = otherProperty;
+ }
+ public string OtherProperty { get; }
+ public override bool RequiresValidationContext => true;
+
+ protected override global::System.ComponentModel.DataAnnotations.ValidationResult? IsValid(object? value, global::System.ComponentModel.DataAnnotations.ValidationContext validationContext)
+ {
+ bool result = true;
+
+ if (validationContext.ObjectInstance is global::ValidationTest.OptionsUsingGeneratedAttributes && OtherProperty == "P5")
+ {
+ result = Equals(value, ((global::ValidationTest.OptionsUsingGeneratedAttributes)validationContext.ObjectInstance).P5);
+ }
+
+ if (!result)
+ {
+ string[]? memberNames = validationContext.MemberName is null ? null : new string[] { validationContext.MemberName };
+ return new global::System.ComponentModel.DataAnnotations.ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
+ }
+
+ return null;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, OtherProperty);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_MaxLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private const int MaxAllowableLength = -1;
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a maximum length of '{1}'.";
+ public __SourceGen__2C497155_MaxLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public __SourceGen__2C497155_MaxLengthAttribute(): base(() => DefaultErrorMessageString) { Length = MaxAllowableLength; }
+ public int Length { get; }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ public override bool IsValid(object? value)
+ {
+ if (Length == 0 || Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.");
+ }
+ if (value == null || MaxAllowableLength == Length)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length <= Length;
+ }
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__2C497155_MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ internal class __SourceGen__2C497155_RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__2C497155_RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
--- /dev/null
+
+ // <auto-generated/>
+ #nullable enable
+ #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
+ namespace ValidationTest
+{
+ partial class OptionsUsingGeneratedAttributesValidator
+ {
+ /// <summary>
+ /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
+ /// </summary>
+ /// <param name="name">The name of the options instance being validated.</param>
+ /// <param name="options">The options instance.</param>
+ /// <returns>Validation result.</returns>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValidationTest.OptionsUsingGeneratedAttributes options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
+ var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P3" : $"{name}.P3";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P5";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P5" : $"{name}.P5";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P5, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P6";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P6" : $"{name}.P6";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P6, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P7";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P7" : $"{name}.P7";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P7, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P8";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P8" : $"{name}.P8";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P8, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P9";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P9" : $"{name}.P9";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P9, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P10";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P10" : $"{name}.P10";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P10, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P11";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P11" : $"{name}.P11";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P11, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P12";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P12" : $"{name}.P12";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P12, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Attributes
+ {
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A1 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ (int)1,
+ (int)3);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MaxLengthAttribute A3 = new __OptionValidationGeneratedAttributes.__SourceGen__MaxLengthAttribute(
+ (int)5);
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute(
+ "P5");
+ }
+}
+namespace __OptionValidationStaticInstances
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ file static class __Validators
+ {
+ }
+}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property, AllowMultiple = false)]
+ file class __SourceGen__CompareAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "'{0}' and '{1}' do not match.";
+ public __SourceGen__CompareAttribute(string otherProperty) : base(() => DefaultErrorMessageString)
+ {
+ if (otherProperty == null)
+ {
+ throw new global::System.ArgumentNullException(nameof(otherProperty));
+ }
+ OtherProperty = otherProperty;
+ }
+ public string OtherProperty { get; }
+ public override bool RequiresValidationContext => true;
+
+ protected override global::System.ComponentModel.DataAnnotations.ValidationResult? IsValid(object? value, global::System.ComponentModel.DataAnnotations.ValidationContext validationContext)
+ {
+ bool result = true;
+
+ if (validationContext.ObjectInstance is global::ValidationTest.OptionsUsingGeneratedAttributes && OtherProperty == "P5")
+ {
+ result = Equals(value, ((global::ValidationTest.OptionsUsingGeneratedAttributes)validationContext.ObjectInstance).P5);
+ }
+
+ if (!result)
+ {
+ string[]? memberNames = validationContext.MemberName is null ? null : new string[] { validationContext.MemberName };
+ return new global::System.ComponentModel.DataAnnotations.ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
+ }
+
+ return null;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, OtherProperty);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MaxLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private const int MaxAllowableLength = -1;
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a maximum length of '{1}'.";
+ public __SourceGen__MaxLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public __SourceGen__MaxLengthAttribute(): base(() => DefaultErrorMessageString) { Length = MaxAllowableLength; }
+ public int Length { get; }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ public override bool IsValid(object? value)
+ {
+ if (Length == 0 || Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MaxLengthAttribute must have a Length value that is greater than zero. Use MaxLength() without parameters to indicate that the string or array can have the maximum allowable length.");
+ }
+ if (value == null || MaxAllowableLength == Length)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length <= Length;
+ }
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else if (value is global::ValidationTest.FakeCount)
+ {
+ length = ((global::ValidationTest.FakeCount)value).Count;
+ }
+ else if (value is global::ValidationTest.FakeCountChild)
+ {
+ length = ((global::ValidationTest.FakeCountChild)value).Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
}
""";
- string generatedSource = """
-
- // <auto-generated/>
- #nullable enable
- #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
- namespace HelloWorld
-{
- partial struct MyOptionsValidator
- {
- /// <summary>
- /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
- /// </summary>
- /// <param name="name">The name of the options instance being validated.</param>
- /// <param name="options">The options instance.</param>
- /// <returns>Validation result.</returns>
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::HelloWorld.MyOptions options)
- {
- global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
- var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
- var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
- var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
-
- context.MemberName = "Val1";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val1" : $"{name}.Val1";
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val1, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- context.MemberName = "Val2";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.Val2" : $"{name}.Val2";
- validationResults.Clear();
- validationAttributes.Clear();
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val2, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
- }
- }
-}
-namespace __OptionValidationStaticInstances
-{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- file static class __Attributes
- {
- internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
-
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A2 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
- (int)1,
- (int)3);
- }
-}
-namespace __OptionValidationStaticInstances
-{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- file static class __Validators
- {
- }
-}
-
-""";
-
var (diagnostics, generatedSources) = await RunGeneratorOnOptionsSource(source);
Assert.Empty(diagnostics);
_ = Assert.Single(generatedSources);
+#if NETCOREAPP
+ string generatedSource = File.ReadAllText(@"Baselines/EmitterWithCustomValidator.netcore.g.cs");
+#else
+ string generatedSource = File.ReadAllText(@"Baselines/EmitterWithCustomValidator.netfx.g.cs");
+#endif // NETCOREAPP
Assert.Equal(generatedSource.Replace("\r\n", "\n"), generatedSources[0].SourceText.ToString().Replace("\r\n", "\n"));
}
Assert.Single(diagnostics);
Assert.Equal(DiagDescriptors.InaccessibleValidationAttribute.Id, diagnostics[0].Id);
string generatedSource = generatedSources[0].SourceText.ToString();
- Assert.Contains("global::System.ComponentModel.DataAnnotations.RangeAttribute", generatedSource);
+ Assert.Contains("__OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute", generatedSource);
Assert.Contains("global::System.ComponentModel.DataAnnotations.RequiredAttribute", generatedSource);
Assert.DoesNotContain("Timeout", generatedSource);
Assert.Empty(diagnostics);
Assert.Single(generatedSources);
- var generatedSource = """
-
- // <auto-generated/>
- #nullable enable
- #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
- namespace Test
-{
- partial class MyOptionsValidator
- {
- /// <summary>
- /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
- /// </summary>
- /// <param name="name">The name of the options instance being validated.</param>
- /// <param name="options">The options instance.</param>
- /// <returns>Validation result.</returns>
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Test.MyOptions options)
- {
- global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
- var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
- var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
- var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);
-
- context.MemberName = "P1";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P1" : $"{name}.P1";
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- context.MemberName = "P2";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P2" : $"{name}.P2";
- validationResults.Clear();
- validationAttributes.Clear();
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- context.MemberName = "P3";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P3" : $"{name}.P3";
- validationResults.Clear();
- validationAttributes.Clear();
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- context.MemberName = "P4";
- context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.P4" : $"{name}.P4";
- validationResults.Clear();
- validationAttributes.Clear();
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
- if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
- {
- (builder ??= new()).AddResults(validationResults);
- }
-
- return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
- }
- }
-}
-namespace __OptionValidationStaticInstances
-{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- file static class __Attributes
- {
- internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
-
- internal static readonly global::System.ComponentModel.DataAnnotations.LengthAttribute A2 = new global::System.ComponentModel.DataAnnotations.LengthAttribute(
- (int)10,
- (int)20);
-
- internal static readonly global::System.ComponentModel.DataAnnotations.AllowedValuesAttribute A3 = new global::System.ComponentModel.DataAnnotations.AllowedValuesAttribute(
- (int)10, (int)20, (int)30);
-
- internal static readonly global::System.ComponentModel.DataAnnotations.DeniedValuesAttribute A4 = new global::System.ComponentModel.DataAnnotations.DeniedValuesAttribute(
- "One", "Ten", "Hundred");
- }
-}
-namespace __OptionValidationStaticInstances
-{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
- file static class __Validators
- {
- }
-}
-
-""";
+ string generatedSource = File.ReadAllText(@"Baselines/DataAnnotationAttributesWithParams.g.cs");
Assert.Equal(generatedSource.Replace("\r\n", "\n"), generatedSources[0].SourceText.ToString().Replace("\r\n", "\n"));
}
- private static CSharpCompilation CreateCompilationForOptionsSource(string assemblyName, string source, string? refAssemblyPath = null)
+ private static CSharpCompilation CreateCompilationForOptionsSource(string assemblyName, string source, string? refAssemblyPath = null, LanguageVersion languageVersion = LanguageVersion.Default)
{
// Ensure the generated source compiles
var compilation = CSharpCompilation
- .Create(Path.GetRandomFileName()+".dll", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
+ .Create($"{assemblyName}.dll", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == "System.Runtime").Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(RequiredAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(OptionsValidatorAttribute).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(IValidateOptions<object>).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).Assembly.Location))
- .AddSyntaxTrees(CSharpSyntaxTree.ParseText(source));
+ .AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(languageVersion)));
if (refAssemblyPath is not null)
{
return result;
}
+
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ [InlineData(LanguageVersion.CSharp10)]
+ [InlineData(LanguageVersion.CSharp11)]
+ public async Task GeneratedAttributesTest(LanguageVersion languageVersion)
+ {
+
+#if NETCOREAPP
+ string lengthAttribute = $$"""
+ [LengthAttribute(1, 3)]
+ public string? P0 { get; set; }
+
+ [LengthAttribute(1, 3)]
+ public FakeCount? P1 { get; set; }
+
+ [LengthAttribute(1, 3)]
+ public FakeCountChild? P2 { get; set; }
+ """;
+#else
+string lengthAttribute = "";
+#endif //NETCOREAPP
+
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Options;
+ using System.ComponentModel.DataAnnotations;
+
+ #nullable enable
+
+ namespace ValidationTest
+ {
+ public class FakeCount
+ {
+ public FakeCount(int count) { Count = count; }
+ public int Count { get; }
+ }
+ public class FakeCountChild : FakeCount
+ {
+ public FakeCountChild(int count) : base(count) { }
+ }
+
+ public class OptionsUsingGeneratedAttributes
+ {
+ {{lengthAttribute}}
+
+ [RangeAttribute(1, 3)]
+ public int P3 { get; set; }
+
+ [MinLengthAttribute(5)]
+ public string? P4 { get; set; }
+
+ [MaxLengthAttribute(5)]
+ public string? P5 { get; set; }
+
+ [CompareAttribute("P5")]
+ public string? P6 { get; set; }
+
+ [MinLengthAttribute(5)]
+ public FakeCount? P7 { get; set; }
+
+ [MinLengthAttribute(5)]
+ public FakeCountChild? P8 { get; set; }
+
+ [MaxLengthAttribute(5)]
+ public FakeCount? P9 { get; set; }
+
+ [MaxLengthAttribute(5)]
+ public FakeCountChild? P10 { get; set; }
+
+ [MinLengthAttribute(5)]
+ public List<string>? P11 { get; set; }
+
+ [MaxLengthAttribute(5)]
+ public List<string>? P12 { get; set; }
+ }
+
+ [OptionsValidator]
+ public sealed partial class OptionsUsingGeneratedAttributesValidator : IValidateOptions<OptionsUsingGeneratedAttributes>
+ {
+ }
+ }
+ """;
+
+ var (diagnostics, generatedSources) = await RunGeneratorOnOptionsSource(source, null, languageVersion);
+ Assert.Empty(diagnostics);
+ Assert.Single(generatedSources);
+
+ string emittedSource = generatedSources[0].SourceText.ToString();
+ SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(emittedSource, new CSharpParseOptions(languageVersion));
+ var diags = syntaxTree.GetDiagnostics().ToArray();
+ Assert.Empty(diags);
+
+#if NETCOREAPP
+ string generatedSource = File.ReadAllText(languageVersion == LanguageVersion.CSharp10 ? @"Baselines/GeneratedAttributesTest.netcore.lang10.g.cs" : @"Baselines/GeneratedAttributesTest.netcore.lang11.g.cs");
+#else
+ string generatedSource = File.ReadAllText(languageVersion == LanguageVersion.CSharp10 ? @"Baselines/GeneratedAttributesTest.netfx.lang10.g.cs" : @"Baselines/GeneratedAttributesTest.netfx.lang11.g.cs");
+#endif // NET8_0_OR_GREATER
+ Assert.Equal(generatedSource.Replace("\r\n", "\n"), emittedSource.Replace("\r\n", "\n"));
+
+ CSharpCompilation compilation = CreateCompilationForOptionsSource(Path.GetRandomFileName(), source + emittedSource, refAssemblyPath: null, languageVersion);
+ var emitResult = compilation.Emit(new MemoryStream());
+
+ Assert.True(emitResult.Success);
+ // Console.WriteLine(emittedSource);
+ }
}
<Compile Include="$(CommonPath)..\tests\SourceGenerators\RoslynTestUtils.cs" Link="SourceGenerators\RoslynTestUtils.cs" />
<Compile Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\gen\DiagDescriptorsBase.cs" Link="gen\DiagDescriptorsBase.cs" />
<Compile Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\gen\DiagDescriptors.cs" Link="gen\DiagDescriptors.cs" />
+ <Compile Remove="Baselines\**\*.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETFramework'">
OutputItemType="Analyzer" ReferenceOutputAssembly="true" SetTargetFramework="TargetFramework=netstandard2.0"/>
</ItemGroup>
+ <ItemGroup>
+ <Content Include="Baselines\**\*">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+
<Target Name="FixIncrementalCoreCompileWithAnalyzers" BeforeTargets="CoreCompile">
<ItemGroup>
<CustomAdditionalCompileInputs Include="@(Analyzer)" />
}, result.Failures);
}
#endif // NET8_0_OR_GREATER
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+ public void TestCustomGeneratedAttributes()
+ {
+ OptionsUsingGeneratedAttributes noFailures = new OptionsUsingGeneratedAttributes()
+ {
+#if NET8_0_OR_GREATER
+ P0 = "123",
+ P11 = new DateTime(2023, 2, 1),
+ P12 = 6,
+ P13 = 9,
+ P14 = new List<string>() { "1", "2" },
+ P15 = new FakeCount(5),
+ P16 = new FakeCountChild(5),
+ P17 = new int[] { 1, 2 },
+ P18 = new List<string>() { "1", "2", "3" },
+ P19 = new FakeCount(3),
+ P20 = new FakeCountChild(3),
+ P23 = new List<string>() { "1", "2", "3", "4" },
+ P24 = new FakeCount(4),
+ P25 = new FakeCountChild(4),
+#endif // NET8_0_OR_GREATER
+ P1 = 2,
+ P2 = "12345",
+ P3 = "12345",
+ P4 = "12345",
+ P5 = 4,
+ P6 = 4,
+ P7 = 15,
+ P8 = 15,
+ P9 = 2.5m,
+ P10 = 14.0,
+ P21 = new int[] { 1, 2, 3 },
+ P22 = new int[] { 1, 2, 3, 4 },
+ P26 = 14.0,
+ };
+ List<ValidationResult> results = new();
+ Assert.True(Validator.TryValidateObject(noFailures, new ValidationContext(noFailures), results, true));
+
+ OptionsUsingGeneratedAttributesValidator validator = new();
+ Assert.True(validator.Validate("OptionsUsingGeneratedAttributes", noFailures).Succeeded);
+
+ OptionsUsingGeneratedAttributes failing = new OptionsUsingGeneratedAttributes()
+ {
+#if NET8_0_OR_GREATER
+ P0 = "",
+ P11 = new DateTime(2023, 1, 1),
+ P12 = 5,
+ P13 = 10,
+ P14 = new List<string>() { "1" },
+ P15 = new FakeCount(1),
+ P16 = new FakeCountChild(11),
+ P17 = new int[] { 1 },
+ P18 = new List<string>() { "1", "2" },
+ P19 = new FakeCount(2),
+ P20 = new FakeCountChild(1),
+ P23 = new List<string>() { "1", "2", "3", "4", "5" },
+ P24 = new FakeCount(5),
+ P25 = new FakeCountChild(5),
+#endif // NET8_0_OR_GREATER
+ P1 = 4,
+ P2 = "1234",
+ P3 = "123456",
+ P4 = "12345",
+ P5 = 10,
+ P6 = 10,
+ P7 = 5,
+ P8 = 5,
+ P9 = 4.0m,
+ P10 = 20.0,
+ P21 = new int[] { 1, 2 },
+ P22 = new int[] { 1, 2, 3, 4, 5 },
+ P26 = 20.0,
+ };
+
+ Assert.False(Validator.TryValidateObject(failing, new ValidationContext(failing), results, true));
+
+ ValidateOptionsResult generatorResult = validator.Validate("OptionsUsingGeneratedAttributes", failing);
+ Assert.True(generatorResult.Failed);
+
+ Assert.Equal(new [] {
+#if NET8_0_OR_GREATER
+ "P0: The field OptionsUsingGeneratedAttributes.P0 must be a string or collection type with a minimum length of '1' and maximum length of '3'.",
+ "P11: The field OptionsUsingGeneratedAttributes.P11 must be between 1/30/2023 12:00:00 AM and 12/30/2023 12:00:00 AM.",
+ "P12: The field OptionsUsingGeneratedAttributes.P12 must be between 5 exclusive and 10.",
+ "P13: The field OptionsUsingGeneratedAttributes.P13 must be between 5 and 10 exclusive.",
+ "P14: The field OptionsUsingGeneratedAttributes.P14 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
+ "P15: The field OptionsUsingGeneratedAttributes.P15 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
+ "P16: The field OptionsUsingGeneratedAttributes.P16 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
+ "P17: The field OptionsUsingGeneratedAttributes.P17 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
+ "P18: The field OptionsUsingGeneratedAttributes.P18 must be a string or array type with a minimum length of '3'.",
+ "P19: The field OptionsUsingGeneratedAttributes.P19 must be a string or array type with a minimum length of '3'.",
+ "P20: The field OptionsUsingGeneratedAttributes.P20 must be a string or array type with a minimum length of '3'.",
+ "P23: The field OptionsUsingGeneratedAttributes.P23 must be a string or array type with a maximum length of '4'.",
+ "P24: The field OptionsUsingGeneratedAttributes.P24 must be a string or array type with a maximum length of '4'.",
+ "P25: The field OptionsUsingGeneratedAttributes.P25 must be a string or array type with a maximum length of '4'.",
+#endif // NET8_0_OR_GREATER
+ "P1: The field OptionsUsingGeneratedAttributes.P1 must be between 1 and 3.",
+ "P2: The field OptionsUsingGeneratedAttributes.P2 must be a string or array type with a minimum length of '5'.",
+ "P3: The field OptionsUsingGeneratedAttributes.P3 must be a string or array type with a maximum length of '5'.",
+ "P4: 'OptionsUsingGeneratedAttributes.P4' and 'P2' do not match.",
+ "P5: The field OptionsUsingGeneratedAttributes.P5 must be between 2 and 8.",
+ "P6: The field OptionsUsingGeneratedAttributes.P6 must be between 2 and 8.",
+ "P7: The field OptionsUsingGeneratedAttributes.P7 must be between 10 and 20.",
+ "P8: The field OptionsUsingGeneratedAttributes.P8 must be between 10 and 20.",
+ "P9: The field OptionsUsingGeneratedAttributes.P9 must be between 1.5 and 3.14.",
+ "P10: The field OptionsUsingGeneratedAttributes.P10 must be between 12.4 and 16.5.",
+ "P21: The field OptionsUsingGeneratedAttributes.P21 must be a string or array type with a minimum length of '3'.",
+ "P22: The field OptionsUsingGeneratedAttributes.P22 must be a string or array type with a maximum length of '4'.",
+ "P26: The field OptionsUsingGeneratedAttributes.P26 must be between 12.4 and 16.5.",
+ }, generatorResult.Failures);
+
+ Assert.Equal(results.Count(), generatorResult.Failures.Count());
+ }
+ }
+
+ public class FakeCount(int count) { public int Count { get { return count; } } }
+ public class FakeCountChild(int count) : FakeCount(count) { }
+
+ public class OptionsUsingGeneratedAttributes
+ {
+#if NET8_0_OR_GREATER
+ [LengthAttribute(1, 3)]
+ public string? P0 { get; set; }
+
+ [RangeAttribute(typeof(DateTime), "01/30/2023", "12/30/2023", ParseLimitsInInvariantCulture = true, ConvertValueInInvariantCulture = true)]
+ public DateTime P11 { get; set; }
+
+ [RangeAttribute(5, 10, MinimumIsExclusive = true)]
+ public int P12 { get; set; }
+
+ [RangeAttribute(5, 10, MaximumIsExclusive = true)]
+ public int P13 { get; set; }
+
+ [LengthAttribute(2, 10)]
+ public List<string> P14 { get; set; }
+
+ [LengthAttribute(2, 10)]
+ public FakeCount P15 { get; set; }
+
+ [LengthAttribute(2, 10)]
+ public FakeCountChild P16 { get; set; }
+
+ [LengthAttribute(2, 10)]
+ public int[] P17 { get; set; }
+
+ [MinLengthAttribute(3)]
+ public List<string> P18 { get; set; }
+
+ [MinLengthAttribute(3)]
+ public FakeCount P19 { get; set; }
+
+ [MinLengthAttribute(3)]
+ public FakeCountChild P20 { get; set; }
+
+ [MaxLengthAttribute(4)]
+ public List<string> P23 { get; set; }
+
+ [MaxLengthAttribute(4)]
+ public FakeCount P24 { get; set; }
+
+ [MaxLengthAttribute(4)]
+ public FakeCountChild P25 { get; set; }
+#endif // NET8_0_OR_GREATER
+
+ [RangeAttribute(1, 3)]
+ public int P1 { get; set; }
+
+ [MinLengthAttribute(5)]
+ public string? P2 { get; set; }
+
+ [MaxLengthAttribute(5)]
+ public string? P3 { get; set; }
+
+ [CompareAttribute("P2")]
+ public string? P4 { get; set; }
+
+ [RangeAttribute(typeof(byte), "2", "8")]
+ public byte P5 { get; set; }
+
+ [RangeAttribute(typeof(sbyte), "2", "8")]
+ public sbyte P6 { get; set; }
+
+ [RangeAttribute(typeof(short), "10", "20")]
+ public short P7 { get; set; }
+
+ [RangeAttribute(typeof(ulong), "10", "20")]
+ public ulong P8 { get; set; }
+
+ [RangeAttribute(typeof(decimal), "1.5", "3.14")]
+ public decimal P9 { get; set; }
+
+ [RangeAttribute(typeof(double), "12.40", "16.50")]
+ public double P10 { get; set; }
+
+ [MinLengthAttribute(3)]
+ public int[] P21 { get; set; }
+
+ [MaxLengthAttribute(4)]
+ public int[] P22 { get; set; }
+
+ [RangeAttribute(typeof(double), "12.40", "16.50")]
+ public double? P26 { get; set; }
+ }
+
+ [OptionsValidator]
+ public partial class OptionsUsingGeneratedAttributesValidator : IValidateOptions<OptionsUsingGeneratedAttributes>
+ {
}
public class MyOptions
{
}
#endif // NET8_0_OR_GREATER
-}
\ No newline at end of file
+
+}
<data name="OptionsUnsupportedLanguageVersionMessage" xml:space="preserve">
<value>The options validation source generator is not available in C# {0}. Please use language version {1} or greater.</value>
</data>
-</root>
\ No newline at end of file
+ <data name="TypeCannotBeUsedWithTheValidationAttributeTitle" xml:space="preserve">
+ <value>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</value>
+ </data>
+ <data name="TypeCannotBeUsedWithTheValidationAttributeMessage" xml:space="preserve">
+ <value>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</value>
+ </data>
+</root>
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ThirdModelNoNamespace options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::FirstModelNoNamespace options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::SecondModelNoNamespace options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::CustomAttr.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Enumeration.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Enumeration.ThirdModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Enumeration.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Enumeration.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::FileScopedNamespace.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::FunnyStrings.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Generics.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Generics.FirstModel<T> options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::MultiModelValidator.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::MultiModelValidator.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.ThirdModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::Nested.Container1.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RandomMembers.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RecordTypes.ThirdModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RecordTypes.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RecordTypes.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RecordTypes.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RepeatedTypes.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RepeatedTypes.ThirdModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::RepeatedTypes.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::SelfValidation.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RangeAttributeModelDouble options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RequiredAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.TypeWithoutOptionsValidator options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.AttributePropertyModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.ComplexModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.CustomTypeCustomValidationAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.CustomValidationAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.DataTypeAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.DerivedModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.EmailAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.LeafModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.MultipleAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RangeAttributeModelDate options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RangeAttributeModelDouble options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RangeAttributeModelInt options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RegularExpressionAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.RequiredAttributeModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public static global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValueTypes.SecondModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::ValueTypes.FirstModel options)
{
global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
{
internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
- internal static readonly global::System.ComponentModel.DataAnnotations.MinLengthAttribute A2 = new global::System.ComponentModel.DataAnnotations.MinLengthAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute(
(int)5);
internal static readonly global::CustomAttr.CustomAttribute A3 = new global::CustomAttr.CustomAttribute(
false,
"X");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A5 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)0,
(int)10);
internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A6 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
"\"\r\n\\\\");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A7 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A7 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(double)0.5,
(double)0.9);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A8 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A8 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
typeof(global::System.DateTime),
"1/2/2004",
"3/4/2004");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A9 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A9 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3)
{
ErrorMessage = "ErrorMessage"
};
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A10 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A10 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3)
{
internal static readonly global::System.ComponentModel.DataAnnotations.DataTypeAttribute A15 = new global::System.ComponentModel.DataAnnotations.DataTypeAttribute(
(global::System.ComponentModel.DataAnnotations.DataType)11);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A16 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A16 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A17 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A17 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)3,
(int)5);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A18 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A18 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)5,
(int)9);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A19 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A19 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
typeof(global::System.DateTime),
"1/2/2004",
"3/4/2004")
internal static readonly global::RecordTypes.ThirdValidator V7 = new global::RecordTypes.ThirdValidator();
}
}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
{
internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();
- internal static readonly global::System.ComponentModel.DataAnnotations.MinLengthAttribute A2 = new global::System.ComponentModel.DataAnnotations.MinLengthAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute(
(int)5);
internal static readonly global::CustomAttr.CustomAttribute A3 = new global::CustomAttr.CustomAttribute(
false,
"X");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A5 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)0,
(int)10);
internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A6 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
"\"\r\n\\\\");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A7 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A7 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(double)0.5,
(double)0.9);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A8 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A8 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
typeof(global::System.DateTime),
"1/2/2004",
"3/4/2004");
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A9 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A9 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3)
{
ErrorMessage = "ErrorMessage"
};
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A10 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A10 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3)
{
internal static readonly global::System.ComponentModel.DataAnnotations.DataTypeAttribute A15 = new global::System.ComponentModel.DataAnnotations.DataTypeAttribute(
(global::System.ComponentModel.DataAnnotations.DataType)11);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A16 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A16 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)1,
(int)3);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A17 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A17 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)3,
(int)5);
- internal static readonly global::System.ComponentModel.DataAnnotations.RangeAttribute A18 = new global::System.ComponentModel.DataAnnotations.RangeAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A18 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)5,
(int)9);
internal static readonly global::RecordTypes.ThirdValidator V7 = new global::RecordTypes.ThirdValidator();
}
}
+namespace __OptionValidationGeneratedAttributes
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";
+
+ public __SourceGen__MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }
+ public int Length { get; }
+ public override bool IsValid(object? value)
+ {
+ if (Length < -1)
+ {
+ throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");
+ }
+ if (value == null)
+ {
+ return true;
+ }
+
+ int length;
+ if (value is string stringValue)
+ {
+ length = stringValue.Length;
+ }
+ else if (value is System.Collections.ICollection collectionValue)
+ {
+ length = collectionValue.Count;
+ }
+ else
+ {
+ throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");
+ }
+
+ return length >= Length;
+ }
+ public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);
+ }
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
+ file class __SourceGen__RangeAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ public __SourceGen__RangeAttribute(int minimum, int maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(int);
+ }
+ public __SourceGen__RangeAttribute(double minimum, double maximum) : base()
+ {
+ Minimum = minimum;
+ Maximum = maximum;
+ OperandType = typeof(double);
+ }
+ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base()
+ {
+ OperandType = type;
+ NeedToConvertMinMax = true;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ public object Minimum { get; private set; }
+ public object Maximum { get; private set; }
+ public bool MinimumIsExclusive { get; set; }
+ public bool MaximumIsExclusive { get; set; }
+ public global::System.Type OperandType { get; }
+ public bool ParseLimitsInInvariantCulture { get; set; }
+ public bool ConvertValueInInvariantCulture { get; set; }
+ public override string FormatErrorMessage(string name) =>
+ string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
+ private bool NeedToConvertMinMax { get; }
+ private bool Initialized { get; set; }
+ public override bool IsValid(object? value)
+ {
+ if (!Initialized)
+ {
+ if (Minimum is null || Maximum is null)
+ {
+ throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ if (NeedToConvertMinMax)
+ {
+ System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ }
+ int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
+ if (cmp > 0)
+ {
+ throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'.");
+ }
+ else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive))
+ {
+ throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value.");
+ }
+ Initialized = true;
+ }
+
+ if (value is null or string { Length: 0 })
+ {
+ return true;
+ }
+
+ System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
+ object? convertedValue;
+
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+
+ var min = (global::System.IComparable)Minimum;
+ var max = (global::System.IComparable)Maximum;
+
+ return
+ (MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 : min.CompareTo(convertedValue) <= 0) &&
+ (MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 : max.CompareTo(convertedValue) >= 0);
+ }
+ private string GetValidationErrorMessage()
+ {
+ return (MinimumIsExclusive, MaximumIsExclusive) switch
+ {
+ (false, false) => "The field {0} must be between {1} and {2}.",
+ (true, false) => "The field {0} must be between {1} exclusive and {2}.",
+ (false, true) => "The field {0} must be between {1} and {2} exclusive.",
+ (true, true) => "The field {0} must be between {1} exclusive and {2} exclusive.",
+ };
+ }
+ private object? ConvertValue(object? value, System.Globalization.CultureInfo formatProvider)
+ {
+ if (value is string stringValue)
+ {
+ value = global::System.Convert.ChangeType(stringValue, OperandType, formatProvider);
+ }
+ else
+ {
+ value = global::System.Convert.ChangeType(value, OperandType, formatProvider);
+ }
+ return value;
+ }
+ }
+}
<data name="OptionsUnsupportedLanguageVersionMessage" xml:space="preserve">
<value>The options validation source generator is not available in C# {0}. Please use language version {1} or greater.</value>
</data>
-</root>
\ No newline at end of file
+ <data name="TypeCannotBeUsedWithTheValidationAttributeTitle" xml:space="preserve">
+ <value>The validation attribute is only applicable to properties of type string, array, or ICollection; it cannot be used with other types.</value>
+ </data>
+ <data name="TypeCannotBeUsedWithTheValidationAttributeMessage" xml:space="preserve">
+ <value>The validation attribute {0} should only be applied to properties of type string, array, or ICollection. Using it with the type {1} could lead to runtime failures.</value>
+ </data>
+</root>
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
class Program
{
return -1;
}
+ LocalOptionsValidator localOptionsValidator = new LocalOptionsValidator();
+ OptionsUsingValidationAttributes optionsUsingValidationAttributes = new OptionsUsingValidationAttributes
+ {
+ P1 = "12345",
+ P2 = new List<string> { "1234", "12345" },
+ P3 = "123456",
+ P4 = "12345",
+ P5 = 7
+ };
+
+ ValidateOptionsResult result = localOptionsValidator.Validate("", optionsUsingValidationAttributes);
+ if (result.Failed)
+ {
+ return -2;
+ }
+
return 100;
}
public string OptionString { get; set; }
}
}
+
+public class OptionsUsingValidationAttributes
+{
+ [Required]
+ [MinLength(5)]
+ public string P1 { get; set; }
+
+ [Required]
+ [MaxLength(5)]
+ public List<string> P2 { get; set; }
+
+ [Length(2, 8)]
+ public string P3 { get; set; }
+
+ [Compare("P1")]
+ public string P4 { get; set; }
+
+ [Range(1, 10, MinimumIsExclusive = true, MaximumIsExclusive = true)]
+ public int P5 { get; set; }
+}
+
+[OptionsValidator]
+public partial class LocalOptionsValidator : IValidateOptions<OptionsUsingValidationAttributes>
+{
+}
+
Microsoft.Extensions.DependencyInjection
</AdditionalProjectReferences>
</PropertyGroup>
-
+
<ItemGroup>
<TestConsoleAppSourceFiles Include="ConfigureTests.cs" />
</ItemGroup>
+ <ItemGroup>
+ <!-- reference the options source generator -->
+ <_additionalProjectReference Include="<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\gen\Microsoft.Extensions.Options.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" SetTargetFramework="TargetFramework=netstandard2.0" />" />
+ </ItemGroup>
+
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
</Project>