1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

Options validation source generator (#87587)

This commit is contained in:
Tarek Mahmoud Sayed 2023-06-20 14:56:21 -07:00 committed by GitHub
parent 7f4334efd5
commit de6f07edd2
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 12224 additions and 1 deletions

View file

@ -235,7 +235,24 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1116`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* | | __`SYSLIB1116`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* |
| __`SYSLIB1117`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* | | __`SYSLIB1117`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* |
| __`SYSLIB1118`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* | | __`SYSLIB1118`__ | *_`SYSLIB1100`-`SYSLIB1118` reserved for Microsoft.Extensions.Configuration.Binder.SourceGeneration.* |
| __`SYSLIB1201`__ | Options validation generator: Can't use 'ValidateObjectMembersAttribute' or `ValidateEnumeratedItemsAttribute` on fields or properties with open generic types. |
| __`SYSLIB1202`__ | Options validation generator: A member type has no fields or properties to validate. |
| __`SYSLIB1203`__ | Options validation generator: A type has no fields or properties to validate. |
| __`SYSLIB1204`__ | Options validation generator: A type annotated with `OptionsValidatorAttribute` doesn't implement the necessary interface. |
| __`SYSLIB1205`__ | Options validation generator: A type already includes an implementation of the 'Validate' method. |
| __`SYSLIB1206`__ | Options validation generator: Can't validate private fields or properties. |
| __`SYSLIB1207`__ | Options validation generator: Member type is not enumerable. |
| __`SYSLIB1208`__ | Options validation generator: Validators used for transitive or enumerable validation must have a constructor with no parameters. |
| __`SYSLIB1209`__ | Options validation generator: `OptionsValidatorAttribute` can't be applied to a static class. |
| __`SYSLIB1210`__ | Options validation generator: Null validator type specified for the `ValidateObjectMembersAttribute` or 'ValidateEnumeratedItemsAttribute' attributes. |
| __`SYSLIB1211`__ | Options validation generator: Unsupported circular references in model types. |
| __`SYSLIB1212`__ | Options validation generator: Member potentially missing transitive validation. |
| __`SYSLIB1213`__ | Options validation generator: Member potentially missing enumerable validation. |
| __`SYSLIB1214`__ | *_`SYSLIB1214`-`SYSLIB1218` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1215`__ | *_`SYSLIB1214`-`SYSLIB1218` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1216`__ | *_`SYSLIB1214`-`SYSLIB1218` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1217`__ | *_`SYSLIB1214`-`SYSLIB1218` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
| __`SYSLIB1218`__ | *_`SYSLIB1214`-`SYSLIB1218` reserved for Microsoft.Extensions.Options.SourceGeneration.* |
### Diagnostic Suppressions (`SYSLIBSUPPRESS****`) ### Diagnostic Suppressions (`SYSLIBSUPPRESS****`)

View file

@ -31,6 +31,46 @@ namespace System
[DoesNotReturn] [DoesNotReturn]
#endif #endif
private static void Throw(string? paramName) => throw new ArgumentNullException(paramName); private static void Throw(string? paramName) => throw new ArgumentNullException(paramName);
/// <summary>
/// Throws either an <see cref="System.ArgumentNullException"/> or an <see cref="System.ArgumentException"/>
/// if the specified string is <see langword="null"/> or whitespace respectively.
/// </summary>
/// <param name="argument">String to be checked for <see langword="null"/> or whitespace.</param>
/// <param name="paramName">The name of the parameter being checked.</param>
/// <returns>The original value of <paramref name="argument"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETCOREAPP3_0_OR_GREATER
[return: NotNull]
#endif
public static string IfNullOrWhitespace(
#if NETCOREAPP3_0_OR_GREATER
[NotNull]
#endif
string? argument,
[CallerArgumentExpression(nameof(argument))] string paramName = "")
{
#if !NETCOREAPP3_1_OR_GREATER
if (argument == null)
{
throw new ArgumentNullException(paramName);
}
#endif
if (string.IsNullOrWhiteSpace(argument))
{
if (argument == null)
{
throw new ArgumentNullException(paramName);
}
else
{
throw new ArgumentException(paramName, "Argument is whitespace");
}
}
return argument;
}
} }
} }

View file

@ -0,0 +1,95 @@
// 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 System;
namespace Microsoft.Extensions.Options.Generators
{
internal sealed class DiagDescriptors : DiagDescriptorsBase
{
private const string Category = "Microsoft.Extensions.Options.SourceGeneration";
public static DiagnosticDescriptor CantUseWithGenericTypes { get; } = Make(
id: "SYSLIB1201",
title: SR.CantUseWithGenericTypesTitle,
messageFormat: SR.CantUseWithGenericTypesMessage,
category: Category);
public static DiagnosticDescriptor NoEligibleMember { get; } = Make(
id: "SYSLIB1202",
title: SR.NoEligibleMemberTitle,
messageFormat: SR.NoEligibleMemberMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Warning);
public static DiagnosticDescriptor NoEligibleMembersFromValidator { get; } = Make(
id: "SYSLIB1203",
title: SR.NoEligibleMembersFromValidatorTitle,
messageFormat: SR.NoEligibleMembersFromValidatorMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Warning);
public static DiagnosticDescriptor DoesntImplementIValidateOptions { get; } = Make(
id: "SYSLIB1204",
title: SR.DoesntImplementIValidateOptionsTitle,
messageFormat: SR.DoesntImplementIValidateOptionsMessage,
category: Category);
public static DiagnosticDescriptor AlreadyImplementsValidateMethod { get; } = Make(
id: "SYSLIB1205",
title: SR.AlreadyImplementsValidateMethodTitle,
messageFormat: SR.AlreadyImplementsValidateMethodMessage,
category: Category);
public static DiagnosticDescriptor MemberIsInaccessible { get; } = Make(
id: "SYSLIB1206",
title: SR.MemberIsInaccessibleTitle,
messageFormat: SR.MemberIsInaccessibleMessage,
category: Category);
public static DiagnosticDescriptor NotEnumerableType { get; } = Make(
id: "SYSLIB1207",
title: SR.NotEnumerableTypeTitle,
messageFormat: SR.NotEnumerableTypeMessage,
category: Category);
public static DiagnosticDescriptor ValidatorsNeedSimpleConstructor { get; } = Make(
id: "SYSLIB1208",
title: SR.ValidatorsNeedSimpleConstructorTitle,
messageFormat: SR.ValidatorsNeedSimpleConstructorMessage,
category: Category);
public static DiagnosticDescriptor CantBeStaticClass { get; } = Make(
id: "SYSLIB1209",
title: SR.CantBeStaticClassTitle,
messageFormat: SR.CantBeStaticClassMessage,
category: Category);
public static DiagnosticDescriptor NullValidatorType { get; } = Make(
id: "SYSLIB1210",
title: SR.NullValidatorTypeTitle,
messageFormat: SR.NullValidatorTypeMessage,
category: Category);
public static DiagnosticDescriptor CircularTypeReferences { get; } = Make(
id: "SYSLIB1211",
title: SR.CircularTypeReferencesTitle,
messageFormat: SR.CircularTypeReferencesMessage,
category: Category);
public static DiagnosticDescriptor PotentiallyMissingTransitiveValidation { get; } = Make(
id: "SYSLIB1212",
title: SR.PotentiallyMissingTransitiveValidationTitle,
messageFormat: SR.PotentiallyMissingTransitiveValidationMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Warning);
public static DiagnosticDescriptor PotentiallyMissingEnumerableValidation { get; } = Make(
id: "SYSLIB1213",
title: SR.PotentiallyMissingEnumerableValidationTitle,
messageFormat: SR.PotentiallyMissingEnumerableValidationMessage,
category: Category,
defaultSeverity: DiagnosticSeverity.Warning);
}
}

View file

@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using Microsoft.CodeAnalysis;
namespace Microsoft.Extensions.Options.Generators
{
#pragma warning disable CA1052 // Static holder types should be Static or NotInheritable
internal class DiagDescriptorsBase
#pragma warning restore CA1052
{
protected static DiagnosticDescriptor Make(
string id,
string title,
string messageFormat,
string category,
DiagnosticSeverity defaultSeverity = DiagnosticSeverity.Error,
bool isEnabledByDefault = true)
{
return new(
id,
title,
messageFormat,
category,
defaultSeverity,
isEnabledByDefault);
}
}
}

View file

@ -0,0 +1,387 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Microsoft.Extensions.Options.Generators
{
/// <summary>
/// Emits option validation.
/// </summary>
internal sealed class Emitter : EmitterBase
{
private const string StaticValidationAttributeHolderClassName = "__Attributes";
private const string StaticValidatorHolderClassName = "__Validators";
private const string StaticFieldHolderClassesNamespace = "__OptionValidationStaticInstances";
private const string StaticValidationAttributeHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{StaticValidationAttributeHolderClassName}";
private const string StaticValidatorHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{StaticValidatorHolderClassName}";
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 sealed record StaticFieldInfo(string FieldTypeFQN, int FieldOrder, string FieldName, IList<string> InstantiationLines);
public string Emit(
IEnumerable<ValidatorType> validatorTypes,
CancellationToken cancellationToken)
{
var staticValidationAttributesDict = new Dictionary<string, StaticFieldInfo>();
var staticValidatorsDict = new Dictionary<string, StaticFieldInfo>();
foreach (var vt in validatorTypes.OrderBy(static lt => lt.Namespace + "." + lt.Name))
{
cancellationToken.ThrowIfCancellationRequested();
GenValidatorType(vt, ref staticValidationAttributesDict, ref staticValidatorsDict);
}
GenStaticClassWithStaticReadonlyFields(staticValidationAttributesDict.Values, StaticFieldHolderClassesNamespace, StaticValidationAttributeHolderClassName);
GenStaticClassWithStaticReadonlyFields(staticValidatorsDict.Values, StaticFieldHolderClassesNamespace, StaticValidatorHolderClassName);
return Capture();
}
private void GenValidatorType(ValidatorType vt, ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict, ref Dictionary<string, StaticFieldInfo> staticValidatorsDict)
{
if (vt.Namespace.Length > 0)
{
OutLn($"namespace {vt.Namespace}");
OutOpenBrace();
}
foreach (var p in vt.ParentTypes)
{
OutLn(p);
OutOpenBrace();
}
if (vt.IsSynthetic)
{
OutGeneratedCodeAttribute();
OutLn($"internal sealed partial {vt.DeclarationKeyword} {vt.Name}");
}
else
{
OutLn($"partial {vt.DeclarationKeyword} {vt.Name}");
}
OutOpenBrace();
for (var i = 0; i < vt.ModelsToValidate.Count; i++)
{
var modelToValidate = vt.ModelsToValidate[i];
GenModelValidationMethod(modelToValidate, vt.IsSynthetic, ref staticValidationAttributesDict, ref staticValidatorsDict);
}
OutCloseBrace();
foreach (var _ in vt.ParentTypes)
{
OutCloseBrace();
}
if (vt.Namespace.Length > 0)
{
OutCloseBrace();
}
}
private void GenStaticClassWithStaticReadonlyFields(IEnumerable<StaticFieldInfo> staticFields, string classNamespace, string className)
{
OutLn($"namespace {classNamespace}");
OutOpenBrace();
OutGeneratedCodeAttribute();
OutLn($"internal static class {className}");
OutOpenBrace();
var staticValidationAttributes = staticFields
.OrderBy(x => x.FieldOrder)
.ToArray();
for (var i = 0; i < staticValidationAttributes.Length; i++)
{
var attributeInstance = staticValidationAttributes[i];
OutIndent();
Out($"internal static readonly {attributeInstance.FieldTypeFQN} {attributeInstance.FieldName} = ");
for (var j = 0; j < attributeInstance.InstantiationLines.Count; j++)
{
var line = attributeInstance.InstantiationLines[j];
Out(line);
if (j != attributeInstance.InstantiationLines.Count - 1)
{
OutLn();
OutIndent();
}
else
{
Out(';');
}
}
OutLn();
if (i != staticValidationAttributes.Length - 1)
{
OutLn();
}
}
OutCloseBrace();
OutCloseBrace();
}
private void GenModelSelfValidationIfNecessary(ValidatedModel modelToValidate)
{
if (modelToValidate.SelfValidates)
{
OutLn($"builder.AddResults(((global::System.ComponentModel.DataAnnotations.IValidatableObject)options).Validate(context));");
OutLn();
}
}
private void GenModelValidationMethod(
ValidatedModel modelToValidate,
bool makeStatic,
ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict,
ref Dictionary<string, StaticFieldInfo> staticValidatorsDict)
{
OutLn($"/// <summary>");
OutLn($"/// Validates a specific named options instance (or all when <paramref name=\"name\"/> is <see langword=\"null\" />).");
OutLn($"/// </summary>");
OutLn($"/// <param name=\"name\">The name of the options instance being validated.</param>");
OutLn($"/// <param name=\"options\">The options instance.</param>");
OutLn($"/// <returns>Validation result.</returns>");
OutGeneratedCodeAttribute();
OutLn($"public {(makeStatic ? "static " : string.Empty)}global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, {modelToValidate.Name} options)");
OutOpenBrace();
OutLn($"var baseName = (string.IsNullOrEmpty(name) ? \"{modelToValidate.SimpleName}\" : name) + \".\";");
OutLn($"var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();");
OutLn($"var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);");
int capacity = modelToValidate.MembersToValidate.Max(static vm => vm.ValidationAttributes.Count);
if (capacity > 0)
{
OutLn($"var validationResults = new {StaticListType}<{StaticValidationResultType}>();");
OutLn($"var validationAttributes = new {StaticListType}<{StaticValidationAttributeType}>({capacity});");
}
OutLn();
bool cleanListsBeforeUse = false;
foreach (var vm in modelToValidate.MembersToValidate)
{
if (vm.ValidationAttributes.Count > 0)
{
GenMemberValidation(vm, ref staticValidationAttributesDict, cleanListsBeforeUse);
cleanListsBeforeUse = true;
OutLn();
}
if (vm.TransValidatorType is not null)
{
GenTransitiveValidation(vm, ref staticValidatorsDict);
OutLn();
}
if (vm.EnumerationValidatorType is not null)
{
GenEnumerationValidation(vm, ref staticValidatorsDict);
OutLn();
}
}
GenModelSelfValidationIfNecessary(modelToValidate);
OutLn($"return builder.Build();");
OutCloseBrace();
}
private void GenMemberValidation(ValidatedMember vm, ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict, bool cleanListsBeforeUse)
{
OutLn($"context.MemberName = \"{vm.Name}\";");
OutLn($"context.DisplayName = baseName + \"{vm.Name}\";");
if (cleanListsBeforeUse)
{
OutLn($"validationResults.Clear();");
OutLn($"validationAttributes.Clear();");
}
foreach (var attr in vm.ValidationAttributes)
{
var staticValidationAttributeInstance = GetOrAddStaticValidationAttribute(ref staticValidationAttributesDict, attr);
OutLn($"validationAttributes.Add({StaticValidationAttributeHolderClassFQN}.{staticValidationAttributeInstance.FieldName});");
}
OutLn($"if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.{vm.Name}!, context, validationResults, validationAttributes))");
OutOpenBrace();
OutLn($"builder.AddResults(validationResults);");
OutCloseBrace();
}
private StaticFieldInfo GetOrAddStaticValidationAttribute(ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict, ValidationAttributeInfo attr)
{
var attrInstantiationStatementLines = new List<string>();
if (attr.ConstructorArguments.Count > 0)
{
attrInstantiationStatementLines.Add($"new {attr.AttributeName}(");
for (var i = 0; i < attr.ConstructorArguments.Count; i++)
{
if (i != attr.ConstructorArguments.Count - 1)
{
attrInstantiationStatementLines.Add($"{GetPaddingString(1)}{attr.ConstructorArguments[i]},");
}
else
{
attrInstantiationStatementLines.Add($"{GetPaddingString(1)}{attr.ConstructorArguments[i]})");
}
}
}
else
{
attrInstantiationStatementLines.Add($"new {attr.AttributeName}()");
}
if (attr.Properties.Count > 0)
{
attrInstantiationStatementLines.Add("{");
var propertiesOrderedByKey = attr.Properties
.OrderBy(p => p.Key)
.ToArray();
for (var i = 0; i < propertiesOrderedByKey.Length; i++)
{
var prop = propertiesOrderedByKey[i];
var notLast = i != propertiesOrderedByKey.Length - 1;
attrInstantiationStatementLines.Add($"{GetPaddingString(1)}{prop.Key} = {prop.Value}{(notLast ? "," : string.Empty)}");
}
attrInstantiationStatementLines.Add("}");
}
var instantiationStatement = string.Join(Environment.NewLine, attrInstantiationStatementLines);
if (!staticValidationAttributesDict.TryGetValue(instantiationStatement, out var staticValidationAttributeInstance))
{
var fieldNumber = staticValidationAttributesDict.Count + 1;
staticValidationAttributeInstance = new StaticFieldInfo(
FieldTypeFQN: attr.AttributeName,
FieldOrder: fieldNumber,
FieldName: $"A{fieldNumber}",
InstantiationLines: attrInstantiationStatementLines);
staticValidationAttributesDict.Add(instantiationStatement, staticValidationAttributeInstance);
}
return staticValidationAttributeInstance;
}
private void GenTransitiveValidation(ValidatedMember vm, ref Dictionary<string, StaticFieldInfo> staticValidatorsDict)
{
string callSequence;
if (vm.TransValidateTypeIsSynthetic)
{
callSequence = vm.TransValidatorType!;
}
else
{
var staticValidatorInstance = GetOrAddStaticValidator(ref staticValidatorsDict, vm.TransValidatorType!);
callSequence = $"{StaticValidatorHolderClassFQN}.{staticValidatorInstance.FieldName}";
}
var valueAccess = (vm.IsNullable && vm.IsValueType) ? ".Value" : string.Empty;
if (vm.IsNullable)
{
OutLn($"if (options.{vm.Name} is not null)");
OutOpenBrace();
OutLn($"builder.AddResult({callSequence}.Validate(baseName + \"{vm.Name}\", options.{vm.Name}{valueAccess}));");
OutCloseBrace();
}
else
{
OutLn($"builder.AddResult({callSequence}.Validate(baseName + \"{vm.Name}\", options.{vm.Name}{valueAccess}));");
}
}
private void GenEnumerationValidation(ValidatedMember vm, ref Dictionary<string, StaticFieldInfo> staticValidatorsDict)
{
var valueAccess = (vm.IsValueType && vm.IsNullable) ? ".Value" : string.Empty;
var enumeratedValueAccess = (vm.EnumeratedIsNullable && vm.EnumeratedIsValueType) ? ".Value" : string.Empty;
string callSequence;
if (vm.EnumerationValidatorTypeIsSynthetic)
{
callSequence = vm.EnumerationValidatorType!;
}
else
{
var staticValidatorInstance = GetOrAddStaticValidator(ref staticValidatorsDict, vm.EnumerationValidatorType!);
callSequence = $"{StaticValidatorHolderClassFQN}.{staticValidatorInstance.FieldName}";
}
if (vm.IsNullable)
{
OutLn($"if (options.{vm.Name} is not null)");
}
OutOpenBrace();
OutLn($"var count = 0;");
OutLn($"foreach (var o in options.{vm.Name}{valueAccess})");
OutOpenBrace();
if (vm.EnumeratedIsNullable)
{
OutLn($"if (o is not null)");
OutOpenBrace();
OutLn($"builder.AddResult({callSequence}.Validate(baseName + $\"{vm.Name}[{{count}}]\", o{enumeratedValueAccess}));");
OutCloseBrace();
if (!vm.EnumeratedMayBeNull)
{
OutLn($"else");
OutOpenBrace();
OutLn($"builder.AddError(baseName + $\"{vm.Name}[{{count}}] is null\");");
OutCloseBrace();
}
OutLn($"count++;");
}
else
{
OutLn($"builder.AddResult({callSequence}.Validate(baseName + $\"{vm.Name}[{{count++}}]\", o{enumeratedValueAccess}));");
}
OutCloseBrace();
OutCloseBrace();
}
#pragma warning disable CA1822 // Mark members as static: static should come before non-static, but we want the method to be here
private StaticFieldInfo GetOrAddStaticValidator(ref Dictionary<string, StaticFieldInfo> staticValidatorsDict, string validatorTypeFQN)
#pragma warning restore CA1822
{
if (!staticValidatorsDict.TryGetValue(validatorTypeFQN, out var staticValidatorInstance))
{
var fieldNumber = staticValidatorsDict.Count + 1;
staticValidatorInstance = new StaticFieldInfo(
FieldTypeFQN: validatorTypeFQN,
FieldOrder: fieldNumber,
FieldName: $"V{fieldNumber}",
InstantiationLines: new[] { $"new {validatorTypeFQN}()" });
staticValidatorsDict.Add(validatorTypeFQN, staticValidatorInstance);
}
return staticValidatorInstance;
}
}
}

View file

@ -0,0 +1,111 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Extensions.Options.Generators
{
internal class EmitterBase
{
public static string GeneratedCodeAttribute { get; } = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" +
$"\"{typeof(EmitterBase).Assembly.GetName().Name}\", " +
$"\"{typeof(EmitterBase).Assembly.GetName().Version}\")";
public static string FilePreamble { get; } = @$"
// <auto-generated/>
#nullable enable
#pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
";
private const int DefaultStringBuilderCapacity = 1024;
private const int IndentChars = 4;
private readonly StringBuilder _sb = new(DefaultStringBuilderCapacity);
private readonly string[] _padding = new string[16];
private int _indent;
public EmitterBase(bool emitPreamble = true)
{
var padding = _padding;
for (int i = 0; i < padding.Length; i++)
{
padding[i] = new string(' ', i * IndentChars);
}
if (emitPreamble)
{
Out(FilePreamble);
}
}
protected void OutOpenBrace()
{
OutLn("{");
Indent();
}
protected void OutCloseBrace()
{
Unindent();
OutLn("}");
}
protected void OutCloseBraceWithExtra(string extra)
{
Unindent();
OutIndent();
Out("}");
Out(extra);
OutLn();
}
protected void OutIndent()
{
_ = _sb.Append(_padding[_indent]);
}
protected string GetPaddingString(byte indent)
{
return _padding[indent];
}
protected void OutLn()
{
_ = _sb.AppendLine();
}
protected void OutLn(string line)
{
OutIndent();
_ = _sb.AppendLine(line);
}
protected void OutPP(string line)
{
_ = _sb.AppendLine(line);
}
protected void OutEnumeration(IEnumerable<string> e)
{
bool first = true;
foreach (var item in e)
{
if (!first)
{
Out(", ");
}
Out(item);
first = false;
}
}
protected void Out(string text) => _ = _sb.Append(text);
protected void Out(char ch) => _ = _sb.Append(ch);
protected void Indent() => _indent++;
protected void Unindent() => _indent--;
protected void OutGeneratedCodeAttribute() => OutLn($"[{GeneratedCodeAttribute}]");
protected string Capture() => _sb.ToString();
}
}

View file

@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Extensions.Options.Generators
{
[Generator]
public class Generator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<(TypeDeclarationSyntax TypeSyntax, SemanticModel SemanticModel)> typeDeclarations = context.SyntaxProvider
.ForAttributeWithMetadataName(
SymbolLoader.OptionsValidatorAttribute,
(node, _) => node is TypeDeclarationSyntax,
(context, _) => (TypeSyntax:context.TargetNode as TypeDeclarationSyntax, SemanticModel: context.SemanticModel))
.Where(static m => m.TypeSyntax is not null);
IncrementalValueProvider<(Compilation, ImmutableArray<(TypeDeclarationSyntax TypeSyntax, SemanticModel SemanticModel)>)> compilationAndTypes =
context.CompilationProvider.Combine(typeDeclarations.Collect());
context.RegisterSourceOutput(compilationAndTypes, static (spc, source) => HandleAnnotatedTypes(source.Item1, source.Item2, spc));
}
private static void HandleAnnotatedTypes(Compilation compilation, ImmutableArray<(TypeDeclarationSyntax TypeSyntax, SemanticModel SemanticModel)> types, SourceProductionContext context)
{
if (!SymbolLoader.TryLoad(compilation, out var symbolHolder))
{
// Not eligible compilation
return;
}
var parser = new Parser(compilation, context.ReportDiagnostic, symbolHolder!, context.CancellationToken);
var validatorTypes = parser.GetValidatorTypes(types);
if (validatorTypes.Count > 0)
{
var emitter = new Emitter();
var result = emitter.Emit(validatorTypes, context.CancellationToken);
context.AddSource("Validators.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
}
}

View file

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CLSCompliant>false</CLSCompliant>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<UsingToolXliff>true</UsingToolXliff>
<AnalyzerLanguage>cs</AnalyzerLanguage>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AnalyzerRoslynVersion>4.4</AnalyzerRoslynVersion>
<RoslynApiVersion>$(MicrosoftCodeAnalysisVersion_4_4)</RoslynApiVersion>
<DefineConstants Condition="'$(LaunchDebugger)' == 'true'">$(DefineConstants);ROSLYN4_4_OR_GREATER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion_4_4)" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
<Compile Include="DiagDescriptors.cs" />
<Compile Include="DiagDescriptorsBase.cs" />
<Compile Include="Emitter.cs" />
<Compile Include="EmitterBase.cs" />
<Compile Include="Generator.cs" />
<Compile Include="Model\ValidatedMember.cs" />
<Compile Include="Model\ValidatedModel.cs" />
<Compile Include="Model\ValidationAttributeInfo.cs" />
<Compile Include="Model\ValidatorType.cs" />
<Compile Include="Parser.cs" />
<Compile Include="ParserUtilities.cs" />
<Compile Include="SymbolHolder.cs" />
<Compile Include="SymbolLoader.cs" />
<Compile Include="TypeDeclarationSyntaxReceiver.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace Microsoft.Extensions.Options.Generators
{
internal sealed record class ValidatedMember(
string Name,
List<ValidationAttributeInfo> ValidationAttributes,
string? TransValidatorType,
bool TransValidateTypeIsSynthetic,
string? EnumerationValidatorType,
bool EnumerationValidatorTypeIsSynthetic,
bool IsNullable,
bool IsValueType,
bool EnumeratedIsNullable,
bool EnumeratedIsValueType,
bool EnumeratedMayBeNull);
}

View file

@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace Microsoft.Extensions.Options.Generators
{
internal sealed record class ValidatedModel(
string Name,
string SimpleName,
bool SelfValidates,
List<ValidatedMember> MembersToValidate);
}

View file

@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace Microsoft.Extensions.Options.Generators
{
internal sealed record class ValidationAttributeInfo(string AttributeName)
{
public List<string> ConstructorArguments { get; } = new();
public Dictionary<string, string> Properties { get; } = new();
}
}

View file

@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
namespace Microsoft.Extensions.Options.Generators
{
internal sealed record class ValidatorType(
string Namespace,
string Name,
string NameWithoutGenerics,
string DeclarationKeyword,
List<string> ParentTypes,
bool IsSynthetic,
IList<ValidatedModel> ModelsToValidate);
}

View file

@ -0,0 +1,664 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.Extensions.Options.Generators
{
/// <summary>
/// Holds an internal parser class that extracts necessary information for generating IValidateOptions.
/// </summary>
internal sealed class Parser
{
private const int NumValidationMethodArgs = 2;
private readonly CancellationToken _cancellationToken;
private readonly Compilation _compilation;
private readonly Action<Diagnostic> _reportDiagnostic;
private readonly SymbolHolder _symbolHolder;
private readonly Dictionary<ITypeSymbol, ValidatorType> _synthesizedValidators = new(SymbolEqualityComparer.Default);
private readonly HashSet<ITypeSymbol> _visitedModelTypes = new(SymbolEqualityComparer.Default);
public Parser(
Compilation compilation,
Action<Diagnostic> reportDiagnostic,
SymbolHolder symbolHolder,
CancellationToken cancellationToken)
{
_compilation = compilation;
_cancellationToken = cancellationToken;
_reportDiagnostic = reportDiagnostic;
_symbolHolder = symbolHolder;
}
public IReadOnlyList<ValidatorType> GetValidatorTypes(IEnumerable<(TypeDeclarationSyntax TypeSyntax, SemanticModel SemanticModel)> classes)
{
var results = new List<ValidatorType>();
foreach (var group in classes.GroupBy(x => x.TypeSyntax.SyntaxTree))
{
SemanticModel? sm = null;
foreach (var typeDec in group)
{
TypeDeclarationSyntax syntax = typeDec.TypeSyntax;
_cancellationToken.ThrowIfCancellationRequested();
sm ??= typeDec.SemanticModel;
var validatorType = sm.GetDeclaredSymbol(syntax) as ITypeSymbol;
if (validatorType is not null)
{
if (validatorType.IsStatic)
{
Diag(DiagDescriptors.CantBeStaticClass, syntax.GetLocation());
continue;
}
_visitedModelTypes.Clear();
var modelTypes = GetModelTypes(validatorType);
if (modelTypes.Count == 0)
{
// validator doesn't implement IValidateOptions
Diag(DiagDescriptors.DoesntImplementIValidateOptions, syntax.GetLocation(), validatorType.Name);
continue;
}
var modelsValidatorTypeValidates = new List<ValidatedModel>(modelTypes.Count);
foreach (var modelType in modelTypes)
{
if (modelType.Kind == SymbolKind.ErrorType)
{
// the compiler will report this error for us
continue;
}
else
{
// keep track of the models we look at, to detect loops
_ = _visitedModelTypes.Add(modelType.WithNullableAnnotation(NullableAnnotation.None));
}
if (AlreadyImplementsValidateMethod(validatorType, modelType))
{
// this type already implements a validation function, we can't auto-generate a new one
Diag(DiagDescriptors.AlreadyImplementsValidateMethod, syntax.GetLocation(), validatorType.Name);
continue;
}
var membersToValidate = GetMembersToValidate(modelType, true);
if (membersToValidate.Count == 0)
{
// this type lacks any eligible members
Diag(DiagDescriptors.NoEligibleMembersFromValidator, syntax.GetLocation(), modelType.ToString(), validatorType.ToString());
continue;
}
modelsValidatorTypeValidates.Add(new ValidatedModel(
GetFQN(modelType),
modelType.Name,
ModelSelfValidates(modelType),
membersToValidate));
}
string keyword = GetTypeKeyword(validatorType);
// following code establishes the containment hierarchy for the generated type in terms of nested types
var parents = new List<string>();
var parent = syntax.Parent as TypeDeclarationSyntax;
while (parent is not null && IsAllowedKind(parent.Kind()))
{
parents.Add($"partial {GetTypeKeyword(parent)} {parent.Identifier}{parent.TypeParameterList} {parent.ConstraintClauses}");
parent = parent.Parent as TypeDeclarationSyntax;
}
parents.Reverse();
results.Add(new ValidatorType(
validatorType.ContainingNamespace.IsGlobalNamespace ? string.Empty : validatorType.ContainingNamespace.ToString(),
GetMinimalFQN(validatorType),
GetMinimalFQNWithoutGenerics(validatorType),
keyword,
parents,
false,
modelsValidatorTypeValidates));
}
}
}
results.AddRange(_synthesizedValidators.Values);
_synthesizedValidators.Clear();
return results;
}
private static bool IsAllowedKind(SyntaxKind kind) =>
kind == SyntaxKind.ClassDeclaration ||
kind == SyntaxKind.StructDeclaration ||
kind == SyntaxKind.RecordStructDeclaration ||
kind == SyntaxKind.RecordDeclaration;
private static string GetTypeKeyword(ITypeSymbol type)
{
if (type.IsReferenceType)
{
return type.IsRecord ? "record class" : "class";
}
return type.IsRecord ? "record struct" : "struct";
}
private static string GetTypeKeyword(TypeDeclarationSyntax type) =>
type.Kind() switch
{
SyntaxKind.ClassDeclaration => "class",
SyntaxKind.RecordDeclaration => "record class",
SyntaxKind.RecordStructDeclaration => "record struct",
_ => type.Keyword.ValueText,
};
private static string GetFQN(ISymbol type)
=> type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier));
private static string GetMinimalFQN(ISymbol type)
=> type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters));
private static string GetMinimalFQNWithoutGenerics(ISymbol type)
=> type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
/// <summary>
/// Checks whether the given validator already implement the IValidationOptions&gt;T&lt; interface.
/// </summary>
private static bool AlreadyImplementsValidateMethod(INamespaceOrTypeSymbol validatorType, ISymbol modelType)
=> validatorType
.GetMembers("Validate")
.Where(m => m.Kind == SymbolKind.Method)
.Select(m => (IMethodSymbol)m)
.Any(m => m.Parameters.Length == NumValidationMethodArgs
&& m.Parameters[0].Type.SpecialType == SpecialType.System_String
&& SymbolEqualityComparer.Default.Equals(m.Parameters[1].Type, modelType));
/// <summary>
/// Checks whether the given type contain any unbound generic type arguments.
/// </summary>
private static bool HasOpenGenerics(ITypeSymbol type, out string genericType)
{
if (type is INamedTypeSymbol mt)
{
if (mt.IsGenericType)
{
foreach (var ta in mt.TypeArguments)
{
if (ta.TypeKind == TypeKind.TypeParameter)
{
genericType = ta.Name;
return true;
}
}
}
}
else if (type is ITypeParameterSymbol)
{
genericType = type.Name;
return true;
}
else if (type is IArrayTypeSymbol ats)
{
return HasOpenGenerics(ats.ElementType, out genericType);
}
genericType = string.Empty;
return false;
}
private ITypeSymbol? GetEnumeratedType(ITypeSymbol type)
{
if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
// extract the T from a Nullable<T>
type = ((INamedTypeSymbol)type).TypeArguments[0];
}
foreach (var implementingInterface in type.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(implementingInterface.OriginalDefinition, _compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)))
{
return implementingInterface.TypeArguments.First();
}
}
return null;
}
private List<ValidatedMember> GetMembersToValidate(ITypeSymbol modelType, bool speculate)
{
// make a list of the most derived members in the model type
if (modelType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
// extract the T from a Nullable<T>
modelType = ((INamedTypeSymbol)modelType).TypeArguments[0];
}
var members = modelType.GetMembers().ToList();
var addedMembers = new HashSet<string>(members.Select(m => m.Name));
var baseType = modelType.BaseType;
while (baseType is not null && baseType.SpecialType != SpecialType.System_Object)
{
var baseMembers = baseType.GetMembers().Where(m => !addedMembers.Contains(m.Name));
members.AddRange(baseMembers);
addedMembers.UnionWith(baseMembers.Select(m => m.Name));
baseType = baseType.BaseType;
}
var membersToValidate = new List<ValidatedMember>();
foreach (var member in members)
{
var memberInfo = GetMemberInfo(member, speculate);
if (memberInfo is not null)
{
if (member.DeclaredAccessibility != Accessibility.Public && member.DeclaredAccessibility != Accessibility.Internal)
{
Diag(DiagDescriptors.MemberIsInaccessible, member.Locations.First(), member.Name);
continue;
}
membersToValidate.Add(memberInfo);
}
}
return membersToValidate;
}
private ValidatedMember? GetMemberInfo(ISymbol member, bool speculate)
{
ITypeSymbol memberType;
switch (member)
{
case IPropertySymbol prop:
memberType = prop.Type;
break;
case IFieldSymbol field:
if (field.AssociatedSymbol is not null)
{
// a backing field for a property, don't need those
return null;
}
memberType = field.Type;
break;
default:
// we only care about properties and fields
return null;
}
var validationAttrs = new List<ValidationAttributeInfo>();
string? transValidatorTypeName = null;
string? enumerationValidatorTypeName = null;
var enumeratedIsNullable = false;
var enumeratedIsValueType = false;
var enumeratedMayBeNull = false;
var transValidatorIsSynthetic = false;
var enumerationValidatorIsSynthetic = false;
foreach (var attribute in member.GetAttributes().Where(a => a.AttributeClass is not null))
{
var attributeType = attribute.AttributeClass!;
var attrLoc = attribute.ApplicationSyntaxReference?.GetSyntax().GetLocation();
if (SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.ValidateObjectMembersAttributeSymbol))
{
if (HasOpenGenerics(memberType, out var genericType))
{
Diag(DiagDescriptors.CantUseWithGenericTypes, attrLoc, genericType);
#pragma warning disable S1226 // Method parameters, caught exceptions and foreach variables' initial values should not be ignored
speculate = false;
#pragma warning restore S1226 // Method parameters, caught exceptions and foreach variables' initial values should not be ignored
continue;
}
if (attribute.ConstructorArguments.Length == 1)
{
var transValidatorType = attribute.ConstructorArguments[0].Value as INamedTypeSymbol;
if (transValidatorType is not null)
{
if (CanValidate(transValidatorType, memberType))
{
if (transValidatorType.Constructors.Where(c => !c.Parameters.Any()).Any())
{
transValidatorTypeName = transValidatorType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}
else
{
Diag(DiagDescriptors.ValidatorsNeedSimpleConstructor, attrLoc, transValidatorType.Name);
}
}
else
{
Diag(DiagDescriptors.DoesntImplementIValidateOptions, attrLoc, transValidatorType.Name, memberType.Name);
}
}
else
{
Diag(DiagDescriptors.NullValidatorType, attrLoc);
}
}
else if (!_visitedModelTypes.Add(memberType.WithNullableAnnotation(NullableAnnotation.None)))
{
Diag(DiagDescriptors.CircularTypeReferences, attrLoc, memberType.ToString());
speculate = false;
continue;
}
if (transValidatorTypeName == null)
{
transValidatorIsSynthetic = true;
transValidatorTypeName = AddSynthesizedValidator(memberType, member);
}
// pop the stack
_ = _visitedModelTypes.Remove(memberType.WithNullableAnnotation(NullableAnnotation.None));
}
else if (SymbolEqualityComparer.Default.Equals(attributeType, _symbolHolder.ValidateEnumeratedItemsAttributeSymbol))
{
var enumeratedType = GetEnumeratedType(memberType);
if (enumeratedType == null)
{
Diag(DiagDescriptors.NotEnumerableType, attrLoc, memberType);
speculate = false;
continue;
}
enumeratedIsNullable = enumeratedType.IsReferenceType || enumeratedType.NullableAnnotation == NullableAnnotation.Annotated;
enumeratedIsValueType = enumeratedType.IsValueType;
enumeratedMayBeNull = enumeratedType.NullableAnnotation == NullableAnnotation.Annotated;
if (HasOpenGenerics(enumeratedType, out var genericType))
{
Diag(DiagDescriptors.CantUseWithGenericTypes, attrLoc, genericType);
speculate = false;
continue;
}
if (attribute.ConstructorArguments.Length == 1)
{
var enumerationValidatorType = attribute.ConstructorArguments[0].Value as INamedTypeSymbol;
if (enumerationValidatorType is not null)
{
if (CanValidate(enumerationValidatorType, enumeratedType))
{
if (enumerationValidatorType.Constructors.Where(c => c.Parameters.Length == 0).Any())
{
enumerationValidatorTypeName = enumerationValidatorType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}
else
{
Diag(DiagDescriptors.ValidatorsNeedSimpleConstructor, attrLoc, enumerationValidatorType.Name);
}
}
else
{
Diag(DiagDescriptors.DoesntImplementIValidateOptions, attrLoc, enumerationValidatorType.Name, enumeratedType.Name);
}
}
else
{
Diag(DiagDescriptors.NullValidatorType, attrLoc);
}
}
else if (!_visitedModelTypes.Add(enumeratedType.WithNullableAnnotation(NullableAnnotation.None)))
{
Diag(DiagDescriptors.CircularTypeReferences, attrLoc, enumeratedType.ToString());
speculate = false;
continue;
}
if (enumerationValidatorTypeName == null)
{
enumerationValidatorIsSynthetic = true;
enumerationValidatorTypeName = AddSynthesizedValidator(enumeratedType, member);
}
// pop the stack
_ = _visitedModelTypes.Remove(enumeratedType.WithNullableAnnotation(NullableAnnotation.None));
}
else if (ConvertTo(attributeType, _symbolHolder.ValidationAttributeSymbol))
{
var validationAttr = new ValidationAttributeInfo(attributeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
validationAttrs.Add(validationAttr);
foreach (var constructorArgument in attribute.ConstructorArguments)
{
validationAttr.ConstructorArguments.Add(GetArgumentExpression(constructorArgument.Type!, constructorArgument.Value));
}
foreach (var namedArgument in attribute.NamedArguments)
{
validationAttr.Properties.Add(namedArgument.Key, GetArgumentExpression(namedArgument.Value.Type!, namedArgument.Value.Value));
}
}
}
// generate a warning if the field/property seems like it should be transitively validated
if (transValidatorTypeName == null && speculate && memberType.SpecialType == SpecialType.None)
{
if (!HasOpenGenerics(memberType, out var genericType))
{
var membersToValidate = GetMembersToValidate(memberType, false);
if (membersToValidate.Count > 0)
{
Diag(DiagDescriptors.PotentiallyMissingTransitiveValidation, member.GetLocation(), memberType.Name, member.Name);
}
}
}
// generate a warning if the field/property seems like it should be enumerated
if (enumerationValidatorTypeName == null && speculate)
{
var enumeratedType = GetEnumeratedType(memberType);
if (enumeratedType is not null)
{
if (!HasOpenGenerics(enumeratedType, out var genericType))
{
var membersToValidate = GetMembersToValidate(enumeratedType, false);
if (membersToValidate.Count > 0)
{
Diag(DiagDescriptors.PotentiallyMissingEnumerableValidation, member.GetLocation(), enumeratedType.Name, member.Name);
}
}
}
}
if (validationAttrs.Count > 0 || transValidatorTypeName is not null || enumerationValidatorTypeName is not null)
{
return new(
member.Name,
validationAttrs,
transValidatorTypeName,
transValidatorIsSynthetic,
enumerationValidatorTypeName,
enumerationValidatorIsSynthetic,
memberType.IsReferenceType || memberType.NullableAnnotation == NullableAnnotation.Annotated,
memberType.IsValueType,
enumeratedIsNullable,
enumeratedIsValueType,
enumeratedMayBeNull);
}
return null;
}
private string? AddSynthesizedValidator(ITypeSymbol modelType, ISymbol member)
{
var mt = modelType.WithNullableAnnotation(NullableAnnotation.None);
if (mt.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
// extract the T from a Nullable<T>
mt = ((INamedTypeSymbol)mt).TypeArguments[0];
}
if (_synthesizedValidators.TryGetValue(mt, out var validator))
{
return "global::" + validator.Namespace + "." + validator.Name;
}
var membersToValidate = GetMembersToValidate(mt, true);
if (membersToValidate.Count == 0)
{
// this type lacks any eligible members
Diag(DiagDescriptors.NoEligibleMember, member.GetLocation(), mt.ToString(), member.ToString());
return null;
}
var model = new ValidatedModel(
GetFQN(mt),
mt.Name,
false,
membersToValidate);
var validatorTypeName = "__" + mt.Name + "Validator__";
var result = new ValidatorType(
mt.ContainingNamespace.IsGlobalNamespace ? string.Empty : mt.ContainingNamespace.ToString(),
validatorTypeName,
validatorTypeName,
"class",
new List<string>(),
true,
new[] { model });
_synthesizedValidators[mt] = result;
return "global::" + (result.Namespace.Length > 0 ? result.Namespace + "." + result.Name : result.Name);
}
private bool ConvertTo(ITypeSymbol source, ITypeSymbol dest)
{
var conversion = _compilation.ClassifyConversion(source, dest);
return conversion.IsReference && conversion.IsImplicit;
}
private bool ModelSelfValidates(ITypeSymbol modelType)
{
foreach (var implementingInterface in modelType.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(implementingInterface.OriginalDefinition, _symbolHolder.IValidatableObjectSymbol))
{
return true;
}
}
return false;
}
private List<ITypeSymbol> GetModelTypes(ITypeSymbol validatorType)
{
var result = new List<ITypeSymbol>();
foreach (var implementingInterface in validatorType.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(implementingInterface.OriginalDefinition, _symbolHolder.ValidateOptionsSymbol))
{
result.Add(implementingInterface.TypeArguments.First());
}
}
return result;
}
private bool CanValidate(ITypeSymbol validatorType, ISymbol modelType)
{
foreach (var implementingInterface in validatorType.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(implementingInterface.OriginalDefinition, _symbolHolder.ValidateOptionsSymbol))
{
var t = implementingInterface.TypeArguments.First();
if (SymbolEqualityComparer.Default.Equals(modelType, t))
{
return true;
}
}
}
return false;
}
private string GetArgumentExpression(ITypeSymbol type, object? value)
{
if (value == null)
{
return "null";
}
if (type.SpecialType == SpecialType.System_Boolean)
{
return (bool)value ? "true" : "false";
}
if (SymbolEqualityComparer.Default.Equals(type, _symbolHolder.TypeSymbol) &&
value is INamedTypeSymbol sym)
{
return $"typeof({sym.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})";
}
if (type.SpecialType == SpecialType.System_String)
{
return $@"""{EscapeString(value.ToString())}""";
}
if (type.SpecialType == SpecialType.System_Char)
{
return $@"'{EscapeString(value.ToString())}'";
}
return $"({type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}){Convert.ToString(value, CultureInfo.InvariantCulture)}";
}
private static readonly char[] _specialChars = { '\n', '\r', '"', '\\' };
private static string EscapeString(string s)
{
int index = s.IndexOfAny(_specialChars);
if (index < 0)
{
return s;
}
var sb = new StringBuilder(s.Length);
_ = sb.Append(s, 0, index);
while (index < s.Length)
{
_ = s[index] switch
{
'\n' => sb.Append("\\n"),
'\r' => sb.Append("\\r"),
'"' => sb.Append("\\\""),
'\\' => sb.Append("\\\\"),
var other => sb.Append(other),
};
index++;
}
return sb.ToString();
}
private void Diag(DiagnosticDescriptor desc, Location? location)
{
_reportDiagnostic(Diagnostic.Create(desc, location, Array.Empty<object?>()));
}
private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs)
{
_reportDiagnostic(Diagnostic.Create(desc, location, messageArgs));
}
}
}

View file

@ -0,0 +1,75 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.Extensions.Options.Generators
{
internal static class ParserUtilities
{
internal static AttributeData? GetSymbolAttributeAnnotationOrDefault(ISymbol? attribute, ISymbol symbol)
{
if (attribute is null)
{
return null;
}
var attrs = symbol.GetAttributes();
foreach (var item in attrs)
{
if (SymbolEqualityComparer.Default.Equals(attribute, item.AttributeClass) && item.AttributeConstructor != null)
{
return item;
}
}
return null;
}
internal static bool PropertyHasModifier(ISymbol property, SyntaxKind modifierToSearch, CancellationToken token)
=> property
.DeclaringSyntaxReferences
.Any(x =>
x.GetSyntax(token) is PropertyDeclarationSyntax syntax &&
syntax.Modifiers.Any(m => m.IsKind(modifierToSearch)));
internal static Location? GetLocation(this ISymbol symbol)
{
if (symbol is null)
{
return null;
}
return symbol.Locations.IsDefaultOrEmpty
? null
: symbol.Locations[0];
}
internal static bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest, Compilation comp)
{
var conversion = comp.ClassifyConversion(source, dest);
return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit);
}
internal static bool ImplementsInterface(this ITypeSymbol type, ITypeSymbol interfaceType)
{
foreach (var iface in type.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(interfaceType, iface))
{
return true;
}
}
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;
}
}

View file

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AlreadyImplementsValidateMethodMessage" xml:space="preserve">
<value>Type {0} already implements the Validate method.</value>
</data>
<data name="AlreadyImplementsValidateMethodTitle" xml:space="preserve">
<value>A type already includes an implementation of the 'Validate' method.</value>
</data>
<data name="CantBeStaticClassMessage" xml:space="preserve">
<value>[OptionsValidator] cannot be applied to static class {0}.</value>
</data>
<data name="CantBeStaticClassTitle" xml:space="preserve">
<value>'OptionsValidatorAttribute' can't be applied to a static class.</value>
</data>
<data name="CantUseWithGenericTypesMessage" xml:space="preserve">
<value>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</value>
</data>
<data name="CantUseWithGenericTypesTitle" xml:space="preserve">
<value>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</value>
</data>
<data name="CircularTypeReferencesMessage" xml:space="preserve">
<value>There is a circular type reference involving type {0} preventing it from being used for static validation.</value>
</data>
<data name="CircularTypeReferencesTitle" xml:space="preserve">
<value>Unsupported circular references in model types.</value>
</data>
<data name="DoesntImplementIValidateOptionsMessage" xml:space="preserve">
<value>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</value>
</data>
<data name="DoesntImplementIValidateOptionsTitle" xml:space="preserve">
<value>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</value>
</data>
<data name="MemberIsInaccessibleMessage" xml:space="preserve">
<value>Can't apply validation attributes to private field or property {0}.</value>
</data>
<data name="MemberIsInaccessibleTitle" xml:space="preserve">
<value>Can't validate private fields or properties.</value>
</data>
<data name="NoEligibleMemberMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced from member {1}.</value>
</data>
<data name="NoEligibleMembersFromValidatorMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced by type {1}.</value>
</data>
<data name="NoEligibleMembersFromValidatorTitle" xml:space="preserve">
<value>A type has no fields or properties to validate.</value>
</data>
<data name="NoEligibleMemberTitle" xml:space="preserve">
<value>A member type has no fields or properties to validate.</value>
</data>
<data name="NotEnumerableTypeMessage" xml:space="preserve">
<value>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</value>
</data>
<data name="NotEnumerableTypeTitle" xml:space="preserve">
<value>Member type is not enumerable.</value>
</data>
<data name="NullValidatorTypeMessage" xml:space="preserve">
<value>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</value>
</data>
<data name="NullValidatorTypeTitle" xml:space="preserve">
<value>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationTitle" xml:space="preserve">
<value>Member potentially missing enumerable validation.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationTitle" xml:space="preserve">
<value>Member potentially missing transitive validation.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorMessage" xml:space="preserve">
<value>Validator type {0} doesn't have a parameterless constructor.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorTitle" xml:space="preserve">
<value>Validators used for transitive or enumerable validation must have a constructor with no parameters.</value>
</data>
</root>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="de" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="es" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="fr" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="it" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ja" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ko" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="pl" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="pt-BR" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="ru" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="tr" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="zh-Hans" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="zh-Hant" original="../Strings.resx">
<body>
<trans-unit id="AlreadyImplementsValidateMethodMessage">
<source>Type {0} already implements the Validate method.</source>
<target state="new">Type {0} already implements the Validate method.</target>
<note />
</trans-unit>
<trans-unit id="AlreadyImplementsValidateMethodTitle">
<source>A type already includes an implementation of the 'Validate' method.</source>
<target state="new">A type already includes an implementation of the 'Validate' method.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassMessage">
<source>[OptionsValidator] cannot be applied to static class {0}.</source>
<target state="new">[OptionsValidator] cannot be applied to static class {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantBeStaticClassTitle">
<source>'OptionsValidatorAttribute' can't be applied to a static class.</source>
<target state="new">'OptionsValidatorAttribute' can't be applied to a static class.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesMessage">
<source>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</source>
<target state="new">Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</target>
<note />
</trans-unit>
<trans-unit id="CantUseWithGenericTypesTitle">
<source>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</source>
<target state="new">Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesMessage">
<source>There is a circular type reference involving type {0} preventing it from being used for static validation.</source>
<target state="new">There is a circular type reference involving type {0} preventing it from being used for static validation.</target>
<note />
</trans-unit>
<trans-unit id="CircularTypeReferencesTitle">
<source>Unsupported circular references in model types.</source>
<target state="new">Unsupported circular references in model types.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsMessage">
<source>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</source>
<target state="new">Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</target>
<note />
</trans-unit>
<trans-unit id="DoesntImplementIValidateOptionsTitle">
<source>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</source>
<target state="new">A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleMessage">
<source>Can't apply validation attributes to private field or property {0}.</source>
<target state="new">Can't apply validation attributes to private field or property {0}.</target>
<note />
</trans-unit>
<trans-unit id="MemberIsInaccessibleTitle">
<source>Can't validate private fields or properties.</source>
<target state="new">Can't validate private fields or properties.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberMessage">
<source>Type {0} has no fields or properties to validate, referenced from member {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced from member {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMemberTitle">
<source>A member type has no fields or properties to validate.</source>
<target state="new">A member type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorMessage">
<source>Type {0} has no fields or properties to validate, referenced by type {1}.</source>
<target state="new">Type {0} has no fields or properties to validate, referenced by type {1}.</target>
<note />
</trans-unit>
<trans-unit id="NoEligibleMembersFromValidatorTitle">
<source>A type has no fields or properties to validate.</source>
<target state="new">A type has no fields or properties to validate.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeMessage">
<source>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</source>
<target state="new">[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</target>
<note />
</trans-unit>
<trans-unit id="NotEnumerableTypeTitle">
<source>Member type is not enumerable.</source>
<target state="new">Member type is not enumerable.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeMessage">
<source>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</source>
<target state="new">Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</target>
<note />
</trans-unit>
<trans-unit id="NullValidatorTypeTitle">
<source>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</source>
<target state="new">Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingEnumerableValidationTitle">
<source>Member potentially missing enumerable validation.</source>
<target state="new">Member potentially missing enumerable validation.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationMessage">
<source>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</source>
<target state="new">Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</target>
<note />
</trans-unit>
<trans-unit id="PotentiallyMissingTransitiveValidationTitle">
<source>Member potentially missing transitive validation.</source>
<target state="new">Member potentially missing transitive validation.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorMessage">
<source>Validator type {0} doesn't have a parameterless constructor.</source>
<target state="new">Validator type {0} doesn't have a parameterless constructor.</target>
<note />
</trans-unit>
<trans-unit id="ValidatorsNeedSimpleConstructorTitle">
<source>Validators used for transitive or enumerable validation must have a constructor with no parameters.</source>
<target state="new">Validators used for transitive or enumerable validation must have a constructor with no parameters.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,20 @@
// 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;
namespace Microsoft.Extensions.Options.Generators
{
/// <summary>
/// Holds required symbols for the <see cref="Generator"/>.
/// </summary>
internal sealed record class SymbolHolder(
INamedTypeSymbol OptionsValidatorSymbol,
INamedTypeSymbol ValidationAttributeSymbol,
INamedTypeSymbol DataTypeAttributeSymbol,
INamedTypeSymbol ValidateOptionsSymbol,
INamedTypeSymbol IValidatableObjectSymbol,
INamedTypeSymbol TypeSymbol,
INamedTypeSymbol? ValidateObjectMembersAttributeSymbol,
INamedTypeSymbol? ValidateEnumeratedItemsAttributeSymbol);
}

View file

@ -0,0 +1,68 @@
// 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;
namespace Microsoft.Extensions.Options.Generators
{
internal static class SymbolLoader
{
public const string OptionsValidatorAttribute = "Microsoft.Extensions.Options.OptionsValidatorAttribute";
internal const string ValidationAttribute = "System.ComponentModel.DataAnnotations.ValidationAttribute";
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 TypeOfType = "System.Type";
internal const string ValidateObjectMembersAttribute = "Microsoft.Extensions.Options.ValidateObjectMembersAttribute";
internal const string ValidateEnumeratedItemsAttribute = "Microsoft.Extensions.Options.ValidateEnumeratedItemsAttribute";
public static bool TryLoad(Compilation compilation, out SymbolHolder? symbolHolder)
{
INamedTypeSymbol? GetSymbol(string metadataName, bool optional = false)
{
var symbol = compilation.GetTypeByMetadataName(metadataName);
if (symbol == null && !optional)
{
return null;
}
return symbol;
}
// required
var optionsValidatorSymbol = GetSymbol(OptionsValidatorAttribute);
var validationAttributeSymbol = GetSymbol(ValidationAttribute);
var dataTypeAttributeSymbol = GetSymbol(DataTypeAttribute);
var ivalidatableObjectSymbol = GetSymbol(IValidatableObjectType);
var validateOptionsSymbol = GetSymbol(IValidateOptionsType);
var typeSymbol = GetSymbol(TypeOfType);
#pragma warning disable S1067 // Expressions should not be too complex
if (optionsValidatorSymbol == null ||
validationAttributeSymbol == null ||
dataTypeAttributeSymbol == null ||
ivalidatableObjectSymbol == null ||
validateOptionsSymbol == null ||
typeSymbol == null)
{
symbolHolder = default;
return false;
}
#pragma warning restore S1067 // Expressions should not be too complex
symbolHolder = new(
optionsValidatorSymbol,
validationAttributeSymbol,
dataTypeAttributeSymbol,
validateOptionsSymbol,
ivalidatableObjectSymbol,
typeSymbol,
// optional
GetSymbol(ValidateObjectMembersAttribute, optional: true),
GetSymbol(ValidateEnumeratedItemsAttribute, optional: true));
return true;
}
}
}

View file

@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.Extensions.Options.Generators
{
/// <summary>
/// Class/struct/record declaration syntax receiver for generators.
/// </summary>
internal sealed class TypeDeclarationSyntaxReceiver : ISyntaxReceiver
{
internal static ISyntaxReceiver Create() => new TypeDeclarationSyntaxReceiver();
/// <summary>
/// Gets class/struct/record declaration syntax holders after visiting nodes.
/// </summary>
public ICollection<TypeDeclarationSyntax> TypeDeclarations { get; } = new List<TypeDeclarationSyntax>();
/// <inheritdoc/>
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classSyntax)
{
TypeDeclarations.Add(classSyntax);
}
else if (syntaxNode is StructDeclarationSyntax structSyntax)
{
TypeDeclarations.Add(structSyntax);
}
else if (syntaxNode is RecordDeclarationSyntax recordSyntax)
{
TypeDeclarations.Add(recordSyntax);
}
else if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax)
{
TypeDeclarations.Add(interfaceSyntax);
}
}
}
}

View file

@ -215,6 +215,10 @@ namespace Microsoft.Extensions.Options
public string OptionsName { get { throw null; } } public string OptionsName { get { throw null; } }
public System.Type OptionsType { get { throw null; } } public System.Type OptionsType { get { throw null; } }
} }
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct)]
public sealed class OptionsValidatorAttribute : System.Attribute
{
}
public partial class OptionsWrapper<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : Microsoft.Extensions.Options.IOptions<TOptions> where TOptions : class public partial class OptionsWrapper<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : Microsoft.Extensions.Options.IOptions<TOptions> where TOptions : class
{ {
public OptionsWrapper(TOptions options) { } public OptionsWrapper(TOptions options) { }
@ -282,6 +286,20 @@ namespace Microsoft.Extensions.Options
public virtual void PostConfigure(string? name, TOptions options) { } public virtual void PostConfigure(string? name, TOptions options) { }
public void PostConfigure(TOptions options) { } public void PostConfigure(TOptions options) { }
} }
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field)]
public sealed class ValidateEnumeratedItemsAttribute : System.Attribute
{
public ValidateEnumeratedItemsAttribute() {}
public ValidateEnumeratedItemsAttribute(System.Type validator) { throw null; }
public System.Type? Validator { get {throw null; } }
}
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field)]
public sealed class ValidateObjectMembersAttribute : System.Attribute
{
public ValidateObjectMembersAttribute() {}
public ValidateObjectMembersAttribute(System.Type validator) { throw null; }
public System.Type? Validator { get { throw null; } }
}
public partial class ValidateOptionsResult public partial class ValidateOptionsResult
{ {
public static readonly Microsoft.Extensions.Options.ValidateOptionsResult Skip; public static readonly Microsoft.Extensions.Options.ValidateOptionsResult Skip;

View file

@ -30,4 +30,10 @@
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\gen\Microsoft.Extensions.Options.SourceGeneration.csproj"
ReferenceOutputAssembly="false"
PackAsAnalyzer="true" />
</ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
namespace Microsoft.Extensions.Options
{
/// <summary>
/// Triggers the automatic generation of the implementation of <see cref="Microsoft.Extensions.Options.IValidateOptions{T}" /> at compile time.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class OptionsValidatorAttribute : Attribute
{
}
}

View file

@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Options
{
/// <summary>
/// Marks a field or property to be enumerated, and each enumerated object to be validated.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class ValidateEnumeratedItemsAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ValidateEnumeratedItemsAttribute"/> class.
/// </summary>
/// <remarks>
/// Using this constructor for a field/property tells the code generator to
/// generate validation for the individual members of the enumerable's type.
/// </remarks>
public ValidateEnumeratedItemsAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ValidateEnumeratedItemsAttribute"/> class.
/// </summary>
/// <param name="validator">A type that implements <see cref="IValidateOptions{T}" /> for the enumerable's type.</param>
/// <remarks>
/// Using this constructor for a field/property tells the code generator to use the given type to validate
/// the object held by the enumerable.
/// </remarks>
public ValidateEnumeratedItemsAttribute(Type validator)
{
Validator = validator;
}
/// <summary>
/// Gets the type to use to validate the enumerable's objects.
/// </summary>
public Type? Validator { get; }
}
}

View file

@ -0,0 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Options
{
/// <summary>
/// Marks a field or property to be validated transitively.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class ValidateObjectMembersAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ValidateObjectMembersAttribute"/> class.
/// </summary>
/// <remarks>
/// Using this constructor for a field/property tells the code generator to
/// generate validation for the individual members of the field/property's type.
/// </remarks>
public ValidateObjectMembersAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ValidateObjectMembersAttribute"/> class.
/// </summary>
/// <param name="validator">A type that implements <see cref="IValidateOptions{T}" /> for the field/property's type.</param>
/// <remarks>
/// Using this constructor for a field/property tells the code generator to use the given type to validate
/// the object held by the field/property.
/// </remarks>
public ValidateObjectMembersAttribute(Type validator)
{
Validator = validator;
}
/// <summary>
/// Gets the type to use to validate a field or property.
/// </summary>
public Type? Validator { get; }
}
}

View file

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkMinimum)</TargetFrameworks>
<RoslynApiVersion>$(MicrosoftCodeAnalysisVersion_4_4)</RoslynApiVersion>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- <CompilerGeneratedFilesOutputPath>$(OutputPath)/$(TargetFramework)/Generated</CompilerGeneratedFilesOutputPath> -->
<EnableDefaultItems>true</EnableDefaultItems>
<!-- <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> -->
<DefineConstants>$(DefineConstants);ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER;ROSLYN_4_0_OR_GREATER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<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" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETFramework'">
<ProjectReference Include="$(LibrariesProjectRoot)System.ComponentModel.Annotations\src\System.ComponentModel.Annotations.csproj" SkipUseReferenceAssembly="true" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<Reference Include="System.ComponentModel.DataAnnotations" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\src\Microsoft.Extensions.Options.csproj" SkipUseReferenceAssembly="true" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\gen\Microsoft.Extensions.Options.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>
<Target Name="FixIncrementalCoreCompileWithAnalyzers" BeforeTargets="CoreCompile">
<ItemGroup>
<CustomAdditionalCompileInputs Include="@(Analyzer)" />
</ItemGroup>
</Target>
</Project>

View file

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ErrorMessageResourceName" xml:space="preserve">
<value>ErrorMessageResourceName</value>
</data>
<data name="CantUseWithGenericTypesTitle" xml:space="preserve">
<value>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationTitle" xml:space="preserve">
<value>Member potentially missing enumerable validation.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</value>
</data>
<data name="CircularTypeReferencesMessage" xml:space="preserve">
<value>There is a circular type reference involving type {0} preventing it from being used for static validation.</value>
</data>
<data name="CircularTypeReferencesTitle" xml:space="preserve">
<value>Unsupported circular references in model types.</value>
</data>
<data name="MemberIsInaccessibleMessage" xml:space="preserve">
<value>Can't apply validation attributes to private field or property {0}.</value>
</data>
<data name="NotEnumerableTypeMessage" xml:space="preserve">
<value>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</value>
</data>
<data name="NullValidatorTypeTitle" xml:space="preserve">
<value>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</value>
</data>
<data name="CantUseWithGenericTypesMessage" xml:space="preserve">
<value>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</value>
</data>
<data name="NoEligibleMemberTitle" xml:space="preserve">
<value>A member type has no fields or properties to validate.</value>
</data>
<data name="NoEligibleMembersFromValidatorTitle" xml:space="preserve">
<value>A type has no fields or properties to validate.</value>
</data>
<data name="NotEnumerableTypeTitle" xml:space="preserve">
<value>Member type is not enumerable.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorMessage" xml:space="preserve">
<value>Validator type {0} doesn't have a parameterless constructor.</value>
</data>
<data name="DoesntImplementIValidateOptionsMessage" xml:space="preserve">
<value>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorTitle" xml:space="preserve">
<value>Validators used for transitive or enumerable validation must have a constructor with no parameters.</value>
</data>
<data name="NoEligibleMembersFromValidatorMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced by type {1}.</value>
</data>
<data name="NoEligibleMemberMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced from member {1}.</value>
</data>
<data name="DoesntImplementIValidateOptionsTitle" xml:space="preserve">
<value>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</value>
</data>
<data name="MemberIsInaccessibleTitle" xml:space="preserve">
<value>Can't validate private fields or properties.</value>
</data>
<data name="CantBeStaticClassMessage" xml:space="preserve">
<value>[OptionsValidator] cannot be applied to static class {0}.</value>
</data>
<data name="AlreadyImplementsValidateMethodTitle" xml:space="preserve">
<value>A type already includes an implementation of the 'Validate' method.</value>
</data>
<data name="AlreadyImplementsValidateMethodMessage" xml:space="preserve">
<value>Type {0} already implements the Validate method.</value>
</data>
<data name="CantBeStaticClassTitle" xml:space="preserve">
<value>'OptionsValidatorAttribute' can't be applied to a static class.</value>
</data>
<data name="NullValidatorTypeMessage" xml:space="preserve">
<value>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationTitle" xml:space="preserve">
<value>Member potentially missing transitive validation.</value>
</data>
</root>

View file

@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using SourceGenerators.Tests;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options.Generators;
using Microsoft.Shared.Data.Validation;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class EmitterTests
{
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public async Task TestEmitter()
{
var sources = new List<string>();
#pragma warning disable RS1035 // To allow using the File IO APIs inside the analyzer test
foreach (var file in Directory.GetFiles("TestClasses"))
{
#if NETCOREAPP3_1_OR_GREATER
sources.Add("#define NETCOREAPP3_1_OR_GREATER\n" + File.ReadAllText(file));
#else
sources.Add(File.ReadAllText(file));
#endif
}
var (d, r) = await RoslynTestUtils.RunGenerator(
new Generator(),
new[]
{
Assembly.GetAssembly(typeof(RequiredAttribute))!,
Assembly.GetAssembly(typeof(TimeSpanAttribute))!,
Assembly.GetAssembly(typeof(OptionsValidatorAttribute))!,
Assembly.GetAssembly(typeof(IValidateOptions<object>))!,
},
sources)
.ConfigureAwait(false);
Assert.Empty(d);
_ = Assert.Single(r);
#if NETCOREAPP3_1_OR_GREATER
string baseline = File.ReadAllText(@"Baselines/NetCoreApp/Validators.g.cs");
#else
string baseline = File.ReadAllText(@"Baselines/NetFX/Validators.g.cs");
#endif
string result = r[0].SourceText.ToString();
Assert.Equal(baseline, result);
#pragma warning restore RS1035
}
}

View file

@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
#pragma warning disable CA1716
namespace Microsoft.Shared.Collections;
#pragma warning restore CA1716
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Static field, lifetime matches the process")]
internal sealed class EmptyReadOnlyList<T> : IReadOnlyList<T>, ICollection<T>
{
public static readonly EmptyReadOnlyList<T> Instance = new();
private readonly Enumerator _enumerator = new();
public IEnumerator<T> GetEnumerator() => _enumerator;
IEnumerator IEnumerable.GetEnumerator() => _enumerator;
public int Count => 0;
public T this[int index] => throw new ArgumentOutOfRangeException(nameof(index));
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
{
// nop
}
bool ICollection<T>.Contains(T item) => false;
bool ICollection<T>.IsReadOnly => true;
void ICollection<T>.Add(T item) => throw new NotSupportedException();
bool ICollection<T>.Remove(T item) => false;
void ICollection<T>.Clear()
{
// nop
}
internal sealed class Enumerator : IEnumerator<T>
{
public void Dispose()
{
// nop
}
public void Reset()
{
// nop
}
public bool MoveNext() => false;
public T Current => throw new InvalidOperationException();
object IEnumerator.Current => throw new InvalidOperationException();
}
}

View file

@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
#pragma warning disable CA1716
namespace Microsoft.Shared.Collections;
#pragma warning restore CA1716
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal sealed class EmptyReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>, IDictionary<TKey, TValue>
where TKey : notnull
{
public static readonly EmptyReadOnlyDictionary<TKey, TValue> Instance = new();
public int Count => 0;
public TValue this[TKey key] => throw new KeyNotFoundException();
public bool ContainsKey(TKey key) => false;
public IEnumerable<TKey> Keys => EmptyReadOnlyList<TKey>.Instance;
public IEnumerable<TValue> Values => EmptyReadOnlyList<TValue>.Instance;
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => EmptyReadOnlyList<KeyValuePair<TKey, TValue>>.Instance.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
ICollection<TKey> IDictionary<TKey, TValue>.Keys => Array.Empty<TKey>();
ICollection<TValue> IDictionary<TKey, TValue>.Values => Array.Empty<TValue>();
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => true;
TValue IDictionary<TKey, TValue>.this[TKey key]
{
get => throw new KeyNotFoundException();
set => throw new NotSupportedException();
}
public bool TryGetValue(TKey key, out TValue value)
{
#pragma warning disable CS8601 // The recommended implementation: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue
value = default;
#pragma warning restore
return false;
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
// nop
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
// nop
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value) => throw new NotSupportedException();
bool IDictionary<TKey, TValue>.Remove(TKey key) => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => throw new NotSupportedException();
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) => false;
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => false;
}

View file

@ -0,0 +1,160 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
// using Microsoft.Shared.Diagnostics;
#pragma warning disable CA1716
namespace Microsoft.Shared.Data.Validation;
#pragma warning restore CA1716
/// <summary>
/// Provides boundary validation for <see cref="TimeSpan"/>.
/// </summary>
#if !SHARED_PROJECT
[ExcludeFromCodeCoverage]
#endif
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
[SuppressMessage("Design", "CA1019:Define accessors for attribute arguments", Justification = "Indirectly we are.")]
internal sealed class TimeSpanAttribute : ValidationAttribute
{
/// <summary>
/// Gets the lower bound for time span.
/// </summary>
public TimeSpan Minimum => _minMs.HasValue ? TimeSpan.FromMilliseconds((double)_minMs) : TimeSpan.Parse(_min!, CultureInfo.InvariantCulture);
/// <summary>
/// Gets the upper bound for time span.
/// </summary>
public TimeSpan? Maximum
{
get
{
if (_maxMs.HasValue)
{
return TimeSpan.FromMilliseconds((double)_maxMs);
}
else
{
return _max == null ? null : TimeSpan.Parse(_max, CultureInfo.InvariantCulture);
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the time span validation should exclude the minimum and maximum values.
/// </summary>
/// <remarks>
/// By default the property is set to <c>false</c>.
/// </remarks>
public bool Exclusive { get; set; }
private readonly int? _minMs;
private readonly int? _maxMs;
private readonly string? _min;
private readonly string? _max;
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="minMs">Minimum in milliseconds.</param>
public TimeSpanAttribute(int minMs)
{
_minMs = minMs;
_maxMs = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="minMs">Minimum in milliseconds.</param>
/// <param name="maxMs">Maximum in milliseconds.</param>
public TimeSpanAttribute(int minMs, int maxMs)
{
_minMs = minMs;
_maxMs = maxMs;
}
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="min">Minimum represented as time span string.</param>
public TimeSpanAttribute(string min)
{
_ = ThrowHelper.IfNullOrWhitespace(min);
_min = min;
_max = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="TimeSpanAttribute"/> class.
/// </summary>
/// <param name="min">Minimum represented as time span string.</param>
/// <param name="max">Maximum represented as time span string.</param>
public TimeSpanAttribute(string min, string max)
{
_ = ThrowHelper.IfNullOrWhitespace(min);
_ = ThrowHelper.IfNullOrWhitespace(max);
_min = min;
_max = max;
}
/// <summary>
/// Validates that a given value represents an in-range TimeSpan value.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">Additional context for this validation.</param>
/// <returns>A value indicating success or failure.</returns>
protected override ValidationResult IsValid(object? value, ValidationContext? validationContext)
{
var min = Minimum;
var max = Maximum;
if (min >= max)
{
throw new InvalidOperationException($"{nameof(TimeSpanAttribute)} requires that the minimum value be less than the maximum value (see field {validationContext.GetDisplayName()})");
}
if (value == null)
{
// use the [Required] attribute to force presence
return ValidationResult.Success!;
}
if (value is TimeSpan ts)
{
if (Exclusive && ts <= min)
{
return new ValidationResult($"The field {validationContext.GetDisplayName()} must be > to {min}.", validationContext.GetMemberName());
}
if (ts < min)
{
return new ValidationResult($"The field {validationContext.GetDisplayName()} must be >= to {min}.", validationContext.GetMemberName());
}
if (max.HasValue)
{
if (Exclusive && ts >= max.Value)
{
return new ValidationResult($"The field {validationContext.GetDisplayName()} must be < to {max}.", validationContext.GetMemberName());
}
if (ts > max.Value)
{
return new ValidationResult($"The field {validationContext.GetDisplayName()} must be <= to {max}.", validationContext.GetMemberName());
}
}
return ValidationResult.Success!;
}
throw new InvalidOperationException($"{nameof(TimeSpanAttribute)} can only be used with fields of type TimeSpan (see field {validationContext.GetDisplayName()})");
}
}

View file

@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
#pragma warning disable CA1716
namespace Microsoft.Shared.Data.Validation;
#pragma warning restore CA1716
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal static class ValidationContextExtensions
{
public static string[]? GetMemberName(this ValidationContext? validationContext)
{
#pragma warning disable S1168 // Empty arrays and collections should be returned instead of null
return validationContext?.MemberName is { } memberName
? new[] { memberName }
: null;
#pragma warning restore S1168 // Empty arrays and collections should be returned instead of null
}
public static string GetDisplayName(this ValidationContext? validationContext)
{
return validationContext?.DisplayName ?? string.Empty;
}
}

View file

@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using CustomAttr;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class CustomAttrTests
{
[Fact]
public void Invalid()
{
var firstModel = new FirstModel
{
P1 = 'a',
P2 = 'x',
};
var validator = new FirstValidator();
var vr = validator.Validate("CustomAttr", firstModel);
Utils.VerifyValidateOptionsResult(vr, 2, "P1", "P2");
}
[Fact]
public void Valid()
{
var firstModel = new FirstModel
{
P1 = 'A',
P2 = 'A',
};
var validator = new FirstValidator();
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("CustomAttr", firstModel));
}
}

View file

@ -0,0 +1,93 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Enumeration;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class EnumerationTests
{
[Fact]
public void Invalid()
{
var secondModelC = new SecondModel
{
P6 = "1234",
};
var secondModelB = new SecondModel
{
P6 = "12345",
};
var secondModel = new SecondModel
{
P6 = "1234",
};
ThirdModel? thirdModel = new ThirdModel
{
Value = 11
};
var firstModel = new FirstModel
{
P1 = new[] { secondModel },
P2 = new[] { secondModel, secondModelB, secondModelC },
P51 = new[] { thirdModel }
};
var validator = default(FirstValidator);
var vr = validator.Validate("Enumeration", firstModel);
Utils.VerifyValidateOptionsResult(vr, 4, "P1[0].P6", "P2[0].P6", "P2[2].P6", "P51[0].Value");
}
[Fact]
public void NullElement()
{
var firstModel = new FirstModel
{
P1 = new[] { (SecondModel)null! },
};
var validator = default(FirstValidator);
var vr = validator.Validate("Enumeration", firstModel);
Utils.VerifyValidateOptionsResult(vr, 1, "P1[0]");
}
[Fact]
public void Valid()
{
var secondModel = new SecondModel
{
P6 = "12345",
};
var thirdModelA = new ThirdModel
{
Value = 2
};
var thirdModelB = new ThirdModel
{
Value = 9
};
var firstModel = new FirstModel
{
P1 = new[] { secondModel },
P2 = new[] { secondModel },
P3 = new[] { (SecondModel?)null },
P4 = new[] { thirdModelA, thirdModelB },
P5 = new ThirdModel?[] { thirdModelA, default },
P51 = new ThirdModel?[] { thirdModelB, default }
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("Enumeration", firstModel));
}
}

View file

@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Fields;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class FieldTests
{
[Fact]
public void Invalid()
{
var thirdModel = new ThirdModel
{
P5 = "1234",
};
var secondModel = new SecondModel
{
P4 = "1234",
};
var firstModel = new FirstModel
{
P1 = "1234",
P2 = secondModel,
P3 = thirdModel,
};
var validator = default(FirstValidator);
var vr = validator.Validate("Fields", firstModel);
Utils.VerifyValidateOptionsResult(vr, 3, "P1", "P2.P4", "P3.P5");
}
[Fact]
public void Valid()
{
var thirdModel = new ThirdModel
{
P5 = "12345",
P6 = 1
};
var secondModel = new SecondModel
{
P4 = "12345",
};
var firstModel = new FirstModel
{
P1 = "12345",
P2 = secondModel,
P3 = thirdModel,
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("Fields", firstModel));
}
}

View file

@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using FunnyStrings;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class FunnyStringsTests
{
[Fact]
public void Invalid()
{
var firstModel = new FirstModel
{
P1 = "XXX",
};
var validator = default(FirstValidator);
var vr = validator.Validate("FunnyStrings", firstModel);
Utils.VerifyValidateOptionsResult(vr, 1, "P1");
}
[Fact]
public void Valid()
{
var firstModel = new FirstModel
{
P1 = "\"\r\n\\",
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("FunnyStrings", firstModel));
}
}

View file

@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Generics;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class GenericsTests
{
[Fact]
public void Invalid()
{
var secondModel = new SecondModel
{
P4 = "1234",
};
var firstModel = new FirstModel<int>
{
P1 = "1234",
P3 = secondModel,
};
var validator = new FirstValidator<int>();
var vr = validator.Validate("Generics", firstModel);
Utils.VerifyValidateOptionsResult(vr, 2, "P1", "P3.P4");
}
[Fact]
public void Valid()
{
var secondModel = new SecondModel
{
P4 = "12345",
};
var firstModel = new FirstModel<int>
{
P1 = "12345",
P3 = secondModel,
};
var validator = new FirstValidator<int>();
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("Generics", firstModel));
}
}

View file

@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using MultiModelValidator;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class MultiModelValidatorTests
{
[Fact]
public void Invalid()
{
var secondModel = new SecondModel
{
P3 = "1234",
};
var firstModel = new FirstModel
{
P1 = "1234",
P2 = secondModel,
};
var validator = default(MultiValidator);
var vr = validator.Validate("MultiModelValidator", firstModel);
Utils.VerifyValidateOptionsResult(vr, 2, "P1", "P2.P3");
}
[Fact]
public void Valid()
{
var secondModel = new SecondModel
{
P3 = "12345",
};
var firstModel = new FirstModel
{
P1 = "12345",
P2 = secondModel,
};
var validator = default(MultiValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("MultiModelValidator", firstModel));
}
}

View file

@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if ROSLYN_4_0_OR_GREATER
using Microsoft.Extensions.Options;
using Nested;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class NestedTests
{
[Fact]
public void Invalid()
{
var thirdModel = new Container1.ThirdModel
{
P6 = "1234",
};
var secondModel = new Container1.SecondModel
{
P5 = "1234",
};
var firstModel = new Container1.FirstModel
{
P1 = "1234",
P2 = secondModel,
P3 = thirdModel,
P4 = secondModel,
};
var validator = default(Container2.Container3.FirstValidator);
var vr = validator.Validate("Nested", firstModel);
Utils.VerifyValidateOptionsResult(vr, 4, "P1", "P2.P5", "P3.P6", "P4.P5");
}
[Fact]
public void Valid()
{
var thirdModel = new Container1.ThirdModel
{
P6 = "12345",
};
var secondModel = new Container1.SecondModel
{
P5 = "12345",
};
var firstModel = new Container1.FirstModel
{
P1 = "12345",
P2 = secondModel,
P3 = thirdModel,
P4 = secondModel,
};
var validator = default(Container2.Container3.FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("Nested", firstModel));
}
}
#endif

View file

@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class NoNamespaceTests
{
[Fact]
public void Invalid()
{
var thirdModel = new ThirdModelNoNamespace
{
P5 = "1234",
};
var secondModel = new SecondModelNoNamespace
{
P4 = "1234",
};
var firstModel = new FirstModelNoNamespace
{
P1 = "1234",
P2 = secondModel,
P3 = thirdModel,
};
var validator = new FirstValidatorNoNamespace();
var vr = validator.Validate("NoNamespace", firstModel);
Utils.VerifyValidateOptionsResult(vr, 3, "P1", "P2.P4", "P3.P5");
}
[Fact]
public void Valid()
{
var thirdModel = new ThirdModelNoNamespace
{
P5 = "12345",
};
var secondModel = new SecondModelNoNamespace
{
P4 = "12345",
};
var firstModel = new FirstModelNoNamespace
{
P1 = "12345",
P2 = secondModel,
P3 = thirdModel,
};
var validator = new FirstValidatorNoNamespace();
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("NoNamespace", firstModel));
}
}

View file

@ -0,0 +1,442 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Microsoft.Extensions.Options;
using TestClasses.OptionsValidation;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class OptionsValidationTests
{
[Fact]
public void RequiredAttributeValid()
{
var validModel = new RequiredAttributeModel
{
Val = "val"
};
var modelValidator = new RequiredAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RequiredAttributeInvalid()
{
var validModel = new RequiredAttributeModel
{
Val = null
};
var modelValidator = new RequiredAttributeModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void RegularExpressionAttributeValid()
{
var validModel = new RegularExpressionAttributeModel
{
Val = " "
};
var modelValidator = new RegularExpressionAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RegularExpressionAttributeInvalid()
{
var validModel = new RegularExpressionAttributeModel
{
Val = "Not Space"
};
var modelValidator = new RegularExpressionAttributeModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void EmailAttributeValid()
{
var validModel = new EmailAttributeModel
{
Val = "abc@xyz.com"
};
var modelValidator = new EmailAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void EmailAttributeInvalid()
{
var validModel = new EmailAttributeModel
{
Val = "Not Email Address"
};
var modelValidator = new EmailAttributeModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void CustomValidationAttributeValid()
{
var validModel = new CustomValidationAttributeModel
{
Val = "Pass"
};
var modelValidator = new CustomValidationAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void CustomValidationAttributeInvalid()
{
var validModel = new CustomValidationAttributeModel
{
Val = "NOT PASS"
};
var modelValidator = new CustomValidationAttributeModelValidator();
Assert.Throws<ValidationException>(() => modelValidator.Validate(nameof(validModel), validModel));
}
[Fact]
public void DataTypeAttributeValid()
{
var validModel = new DataTypeAttributeModel
{
Val = "ABC"
};
var modelValidator = new DataTypeAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RangeAttributeModelIntValid()
{
var validModel = new RangeAttributeModelInt
{
Val = 1
};
var modelValidator = new RangeAttributeModelIntValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RangeAttributeModelIntInvalid()
{
var validModel = new RangeAttributeModelInt
{
Val = 0
};
var modelValidator = new RangeAttributeModelIntValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void RangeAttributeModelDoubleValid()
{
var validModel = new RangeAttributeModelDouble
{
Val = 0.6
};
var modelValidator = new RangeAttributeModelDoubleValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RangeAttributeModelDoubleInvalid()
{
var validModel = new RangeAttributeModelDouble
{
Val = 0.1
};
var modelValidator = new RangeAttributeModelDoubleValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void RangeAttributeModelDateValid()
{
#if NETCOREAPP3_1_OR_GREATER
// Setting non-invariant culture to check that
// attribute's "ParseLimitsInInvariantCulture" property
// was set up correctly in the validator:
CultureInfo.CurrentCulture = new CultureInfo("cs");
#else
// Setting invariant culture to avoid DateTime parsing discrepancies:
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
#endif
var validModel = new RangeAttributeModelDate
{
Val = new DateTime(day: 3, month: 1, year: 2004)
};
var modelValidator = new RangeAttributeModelDateValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void RangeAttributeModelDateInvalid()
{
var validModel = new RangeAttributeModelDate
{
Val = new DateTime(day: 1, month: 1, year: 2004)
};
var modelValidator = new RangeAttributeModelDateValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void MultipleAttributeModelValid()
{
var validModel = new MultipleAttributeModel
{
Val1 = "abc",
Val2 = 2,
Val3 = 4,
Val4 = 6
};
var modelValidator = new MultipleAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Theory]
[InlineData("", 2, 4, 7)]
[InlineData(null, 2, 4, 7)]
[InlineData("abc", 0, 4, 9)]
[InlineData("abc", 2, 8, 8)]
[InlineData("abc", 2, 4, 10)]
public void MultipleAttributeModelInvalid(string val1, int val2, int val3, int val4)
{
var validModel = new MultipleAttributeModel
{
Val1 = val1,
Val2 = val2,
Val3 = val3,
Val4 = val4
};
var modelValidator = new MultipleAttributeModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void CustomTypeCustomValidationAttributeModelValid()
{
var validModel = new CustomTypeCustomValidationAttributeModel
{
Val = new CustomType { Val1 = "Pass", Val2 = "Pass" }
};
var modelValidator = new CustomTypeCustomValidationAttributeModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void CustomTypeCustomValidationAttributeModelInvalid()
{
var validModel = new CustomTypeCustomValidationAttributeModel
{
Val = new CustomType { Val1 = "Pass", Val2 = "Not Pass" }
};
var modelValidator = new CustomTypeCustomValidationAttributeModelValidator();
Assert.Throws<ValidationException>(() => modelValidator.Validate(nameof(validModel), validModel));
}
[Fact]
public void DerivedModelIsValid()
{
var validModel = new DerivedModel
{
Val = 1,
DerivedVal = "Valid",
VirtualValWithAttr = 1,
VirtualValWithoutAttr = null
};
((RequiredAttributeModel)validModel).Val = "Valid hidden member from base class";
var validator = new DerivedModelValidator();
var result = validator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Theory]
[InlineData(0, "", 1, null, "Valid hidden member from base class")]
[InlineData(null, "Valid", 1, null, "Valid hidden member from base class")]
[InlineData(1, "Valid", null, null, "Valid hidden member from base class")]
public void DerivedModelIsInvalid(int? val, string? derivedVal, int? virtValAttr, int? virtVal, string? hiddenValBaseClass)
{
var invalidModel = new DerivedModel
{
Val = val,
DerivedVal = derivedVal,
VirtualValWithAttr = virtValAttr,
VirtualValWithoutAttr = virtVal
};
((RequiredAttributeModel)invalidModel).Val = hiddenValBaseClass;
var validator = new DerivedModelValidator();
Utils.VerifyValidateOptionsResult(validator.Validate(nameof(invalidModel), invalidModel), 1);
}
[Fact]
public void LeafModelIsValid()
{
var validModel = new LeafModel
{
Val = 1,
DerivedVal = "Valid",
VirtualValWithAttr = null,
VirtualValWithoutAttr = 1
};
((RequiredAttributeModel)validModel).Val = "Valid hidden member from base class";
var validator = new LeafModelValidator();
var result = validator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void ComplexModelValid()
{
var validModel = new ComplexModel
{
ComplexVal = new RequiredAttributeModel { Val = "Valid" }
};
var modelValidator = new ComplexModelValidator();
var result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
validModel = new ComplexModel
{
ValWithoutOptionsValidator = new TypeWithoutOptionsValidator
{
Val1 = "Valid",
Val2 = new DateTime(day: 3, month: 1, year: 2004)
}
};
// Setting invariant culture to avoid DateTime parsing discrepancies:
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
validModel = new ComplexModel
{
ValWithoutOptionsValidator = new TypeWithoutOptionsValidator
{
Val1 = "A",
Val2 = new DateTime(day: 2, month: 2, year: 2004),
YetAnotherComplexVal = new RangeAttributeModelDouble { Val = 0.7 }
}
};
result = modelValidator.Validate(nameof(validModel), validModel);
Assert.Equal(ValidateOptionsResult.Success, result);
}
[Fact]
public void ComplexModelInvalid()
{
var invalidModel = new ComplexModel
{
ComplexVal = new RequiredAttributeModel { Val = null }
};
var modelValidator = new ComplexModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(invalidModel), invalidModel), 1);
invalidModel = new ComplexModel
{
ValWithoutOptionsValidator = new TypeWithoutOptionsValidator { Val1 = "Valid", Val2 = new DateTime(2003, 3, 3) }
};
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(invalidModel), invalidModel), 1);
invalidModel = new ComplexModel
{
ValWithoutOptionsValidator = new TypeWithoutOptionsValidator { Val1 = string.Empty, Val2 = new DateTime(2004, 3, 3) }
};
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(invalidModel), invalidModel), 1);
invalidModel = new ComplexModel
{
ValWithoutOptionsValidator = new TypeWithoutOptionsValidator
{
Val1 = "A",
Val2 = new DateTime(2004, 2, 2),
YetAnotherComplexVal = new RangeAttributeModelDouble { Val = 0.4999 }
}
};
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(invalidModel), invalidModel), 1);
}
[Fact]
public void AttributePropertyModelTestOnErrorMessage()
{
var validModel = new AttributePropertyModel
{
Val1 = 5,
Val2 = 1
};
var modelValidator = new AttributePropertyModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
[Fact]
public void AttributePropertyModelTestOnErrorMessageResource()
{
var validModel = new AttributePropertyModel
{
Val1 = 1,
Val2 = 5
};
var modelValidator = new AttributePropertyModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
}

View file

@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using RandomMembers;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class RandomMembersTests
{
[Fact]
public void Invalid()
{
var firstModel = new FirstModel
{
P1 = "1234",
};
var validator = new FirstValidator();
var vr = validator.Validate("RandomMembers", firstModel);
Utils.VerifyValidateOptionsResult(vr, 1, "P1");
}
[Fact]
public void Valid()
{
var firstModel = new FirstModel
{
P1 = "12345",
};
var validator = new FirstValidator();
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("RandomMembers", firstModel));
}
}

View file

@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if ROSLYN_4_0_OR_GREATER
using Microsoft.Extensions.Options;
using RecordTypes;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class RecordTypesTests
{
[Fact]
public void Invalid()
{
var thirdModel = new ThirdModel
{
P6 = "1234",
};
var secondModel = new SecondModel
{
P5 = "1234",
};
var firstModel = new FirstModel
{
P1 = "1234",
P2 = secondModel,
P3 = secondModel,
P4 = thirdModel,
};
var validator = default(FirstValidator);
var vr = validator.Validate("RecordTypes", firstModel);
Utils.VerifyValidateOptionsResult(vr, 4, "P1", "P2.P5", "P3.P5", "P4.P6");
}
[Fact]
public void Valid()
{
var thirdModel = new ThirdModel
{
P6 = "12345",
};
var secondModel = new SecondModel
{
P5 = "12345",
};
var firstModel = new FirstModel
{
P1 = "12345",
P2 = secondModel,
P3 = secondModel,
P4 = thirdModel,
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("RecordTypes", firstModel));
}
}
#endif

View file

@ -0,0 +1,61 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using RepeatedTypes;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class RepeatedTypesTests
{
[Fact]
public void Invalid()
{
var thirdModel = new ThirdModel
{
P5 = "1234",
};
var secondModel = new SecondModel
{
P4 = thirdModel,
};
var firstModel = new FirstModel
{
P1 = secondModel,
P2 = secondModel,
P3 = thirdModel,
};
var validator = new FirstValidator();
var vr = validator.Validate("RepeatedTypes", firstModel);
Utils.VerifyValidateOptionsResult(vr, 3, "P1.P4.P5", "P2.P4.P5", "P3.P5");
}
[Fact]
public void Valid()
{
var thirdModel = new ThirdModel
{
P5 = "12345",
};
var secondModel = new SecondModel
{
P4 = thirdModel,
};
var firstModel = new FirstModel
{
P1 = secondModel,
P2 = secondModel,
P3 = thirdModel,
};
var validator = new FirstValidator();
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("RepeatedTypes", firstModel));
}
}

View file

@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using SelfValidation;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class SelfValidationTests
{
[Fact]
public void Invalid()
{
var firstModel = new FirstModel
{
P1 = "1234",
};
var validator = default(FirstValidator);
var vr = validator.Validate("SelfValidation", firstModel);
Utils.VerifyValidateOptionsResult(vr, 1, "P1");
}
[Fact]
public void Valid()
{
var firstModel = new FirstModel
{
P1 = "12345",
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("SelfValidation", firstModel));
}
}

View file

@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if NETCOREAPP3_1_OR_GREATER
using System.Linq;
#endif
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
internal static class Utils
{
public static void VerifyValidateOptionsResult(ValidateOptionsResult vr, int expectedErrorCount, params string[] expectedErrorSubstrings)
{
Assert.NotNull(vr);
#if NETCOREAPP3_1_OR_GREATER
var failures = vr.Failures!.ToArray();
#else
var failures = vr.FailureMessage!.Split(';');
#endif
Assert.Equal(expectedErrorCount, failures.Length);
for (int i = 0; i < expectedErrorSubstrings.Length; i++)
{
Assert.Contains(expectedErrorSubstrings[i], failures[i]);
}
}
}

View file

@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Options;
using ValueTypes;
using Xunit;
namespace Microsoft.Gen.OptionsValidation.Test;
public class ValueTypesTests
{
[Fact]
public void Invalid()
{
var secondModel = new SecondModel
{
P4 = "1234",
};
var firstModel = new FirstModel
{
P1 = "1234",
P3 = secondModel,
P2 = secondModel,
P4 = default,
};
var validator = default(FirstValidator);
var vr = validator.Validate("ValueTypes", firstModel);
Utils.VerifyValidateOptionsResult(vr, 3, "P1", "P2.P4", "P3.P4");
}
[Fact]
public void Valid()
{
var secondModel = new SecondModel
{
P4 = "12345",
};
var firstModel = new FirstModel
{
P1 = "12345",
P3 = secondModel,
P2 = secondModel,
P4 = default,
};
var validator = default(FirstValidator);
Assert.Equal(ValidateOptionsResult.Success, validator.Validate("ValueTypes", firstModel));
}
}

View file

@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkMinimum)</TargetFrameworks>
<RoslynApiVersion>$(MicrosoftCodeAnalysisVersion_4_4)</RoslynApiVersion>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- <CompilerGeneratedFilesOutputPath>$(OutputPath)/$(TargetFramework)/Generated</CompilerGeneratedFilesOutputPath> -->
<EnableDefaultItems>true</EnableDefaultItems>
<!-- <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> -->
<DefineConstants>$(DefineConstants);ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER;ROSLYN_4_0_OR_GREATER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
<Compile Include="$(CommonPath)System\ThrowHelper.cs" Link="Common\System\ThrowHelper.cs" />
<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'">
<ProjectReference Include="$(LibrariesProjectRoot)System.ComponentModel.Annotations\src\System.ComponentModel.Annotations.csproj" SkipUseReferenceAssembly="true" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<Reference Include="System.ComponentModel.DataAnnotations" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\src\Microsoft.Extensions.Options.csproj" SkipUseReferenceAssembly="true" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Options\gen\Microsoft.Extensions.Options.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>
<ItemGroup>
<Content Include="TestClasses\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Baselines\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="FixIncrementalCoreCompileWithAnalyzers" BeforeTargets="CoreCompile">
<ItemGroup>
<CustomAdditionalCompileInputs Include="@(Analyzer)" />
</ItemGroup>
</Target>
<!-- <Target Name="MakeMyDir" AfterTargets="PrepareForBuild">
<MakeDir Directories="$(OutputPath)/$(TargetFramework)/Generated" Condition="!Exists('$(OutputPath)/$(TargetFramework)/Generated')" />
</Target> -->
</Project>

View file

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ErrorMessageResourceName" xml:space="preserve">
<value>ErrorMessageResourceName</value>
</data>
<data name="CantUseWithGenericTypesTitle" xml:space="preserve">
<value>Can't use 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' on fields or properties with open generic types.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateEnumeratedItems] which could be an oversight.</value>
</data>
<data name="PotentiallyMissingEnumerableValidationTitle" xml:space="preserve">
<value>Member potentially missing enumerable validation.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationMessage" xml:space="preserve">
<value>Type {0} has validation annotations, but member {1} doesn't specify [ValidateObjectMembers] which could be an oversight.</value>
</data>
<data name="CircularTypeReferencesMessage" xml:space="preserve">
<value>There is a circular type reference involving type {0} preventing it from being used for static validation.</value>
</data>
<data name="CircularTypeReferencesTitle" xml:space="preserve">
<value>Unsupported circular references in model types.</value>
</data>
<data name="MemberIsInaccessibleMessage" xml:space="preserve">
<value>Can't apply validation attributes to private field or property {0}.</value>
</data>
<data name="NotEnumerableTypeMessage" xml:space="preserve">
<value>[ValidateEnumeratedItems] cannot be used on members of type {0} as it doesn't implement IEnumerable&lt;T&gt;.</value>
</data>
<data name="NullValidatorTypeTitle" xml:space="preserve">
<value>Null validator type specified for the 'ValidateObjectMembersAttribute' or 'ValidateEnumeratedItemsAttribute' attributes.</value>
</data>
<data name="CantUseWithGenericTypesMessage" xml:space="preserve">
<value>Can't use [ValidateObjectMembers] or [ValidateEnumeratedItems] on fields or properties with open generic type {0}.</value>
</data>
<data name="NoEligibleMemberTitle" xml:space="preserve">
<value>A member type has no fields or properties to validate.</value>
</data>
<data name="NoEligibleMembersFromValidatorTitle" xml:space="preserve">
<value>A type has no fields or properties to validate.</value>
</data>
<data name="NotEnumerableTypeTitle" xml:space="preserve">
<value>Member type is not enumerable.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorMessage" xml:space="preserve">
<value>Validator type {0} doesn't have a parameterless constructor.</value>
</data>
<data name="DoesntImplementIValidateOptionsMessage" xml:space="preserve">
<value>Type {0} does not implement the required IValidateOptions&lt;{1}&gt; interface.</value>
</data>
<data name="ValidatorsNeedSimpleConstructorTitle" xml:space="preserve">
<value>Validators used for transitive or enumerable validation must have a constructor with no parameters.</value>
</data>
<data name="NoEligibleMembersFromValidatorMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced by type {1}.</value>
</data>
<data name="NoEligibleMemberMessage" xml:space="preserve">
<value>Type {0} has no fields or properties to validate, referenced from member {1}.</value>
</data>
<data name="DoesntImplementIValidateOptionsTitle" xml:space="preserve">
<value>A type annotated with 'OptionsValidatorAttribute' doesn't implement the necessary interface.</value>
</data>
<data name="MemberIsInaccessibleTitle" xml:space="preserve">
<value>Can't validate private fields or properties.</value>
</data>
<data name="CantBeStaticClassMessage" xml:space="preserve">
<value>[OptionsValidator] cannot be applied to static class {0}.</value>
</data>
<data name="AlreadyImplementsValidateMethodTitle" xml:space="preserve">
<value>A type already includes an implementation of the 'Validate' method.</value>
</data>
<data name="AlreadyImplementsValidateMethodMessage" xml:space="preserve">
<value>Type {0} already implements the Validate method.</value>
</data>
<data name="CantBeStaticClassTitle" xml:space="preserve">
<value>'OptionsValidatorAttribute' can't be applied to a static class.</value>
</data>
<data name="NullValidatorTypeMessage" xml:space="preserve">
<value>Null validator type specified in [ValidateObjectMembers] or [ValidateEnumeratedItems] attributes.</value>
</data>
<data name="PotentiallyMissingTransitiveValidationTitle" xml:space="preserve">
<value>Member potentially missing transitive validation.</value>
</data>
</root>

View file

@ -0,0 +1,68 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace CustomAttr
{
#pragma warning disable SA1649
#pragma warning disable SA1402
#pragma warning disable CA1019
#pragma warning disable IDE0052
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class CustomAttribute : ValidationAttribute
{
private readonly char _ch;
private readonly bool _caseSensitive;
private readonly string? _extra;
public CustomAttribute(char ch, bool caseSensitive, string? extra)
{
_ch = ch;
_caseSensitive = caseSensitive;
_extra = extra;
}
protected override ValidationResult IsValid(object? value, ValidationContext? validationContext)
{
if (value == null)
{
return ValidationResult.Success!;
}
if (_caseSensitive)
{
if ((char)value != _ch)
{
return new ValidationResult($"{validationContext?.MemberName} didn't match");
}
}
else
{
if (char.ToUpperInvariant((char)value) != char.ToUpperInvariant(_ch))
{
return new ValidationResult($"{validationContext?.MemberName} didn't match");
}
}
return ValidationResult.Success!;
}
}
public class FirstModel
{
[Custom('A', true, null)]
public char P1 { get; set; }
[Custom('A', false, "X")]
public char P2 { get; set; }
}
[OptionsValidator]
public partial class FirstValidator : IValidateOptions<FirstModel>
{
}
}

View file

@ -0,0 +1,93 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Options;
namespace Enumeration
{
#pragma warning disable SA1649
#pragma warning disable SA1402
public class FirstModel
{
[ValidateEnumeratedItems]
public IList<SecondModel>? P1;
[ValidateEnumeratedItems(typeof(SecondValidator))]
public IList<SecondModel>? P2;
[ValidateEnumeratedItems]
public IList<SecondModel?>? P3;
[ValidateEnumeratedItems]
public IList<ThirdModel>? P4;
[ValidateEnumeratedItems]
public IList<ThirdModel?>? P5;
[ValidateEnumeratedItems]
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1125:Use shorthand for nullable types", Justification = "Testing System>Nullable<T>")]
public IList<Nullable<ThirdModel>>? P51;
[ValidateEnumeratedItems]
public SynteticEnumerable? P6;
[ValidateEnumeratedItems]
public SynteticEnumerable P7;
[ValidateEnumeratedItems]
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1125:Use shorthand for nullable types", Justification = "Testing System>Nullable<T>")]
public Nullable<SynteticEnumerable> P8;
}
public class SecondModel
{
[Required]
[MinLength(5)]
public string P6 = string.Empty;
}
public struct ThirdModel
{
[Range(0, 10)]
public int Value;
}
public struct SynteticEnumerable : IEnumerable<SecondModel>
{
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<SecondModel> GetEnumerator() => new InternalEnumerator();
private class InternalEnumerator : IEnumerator<SecondModel>
{
public SecondModel Current => throw new NotSupportedException();
object IEnumerator.Current => Current;
public void Dispose()
{
// Nothing to dispose...
}
public bool MoveNext() => false;
public void Reset() => throw new NotSupportedException();
}
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
}
[OptionsValidator]
public partial struct SecondValidator : IValidateOptions<SecondModel>
{
}
}

View file

@ -0,0 +1,71 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace Fields
{
#pragma warning disable SA1649
#pragma warning disable SA1402
#pragma warning disable S1186
#pragma warning disable CA1822
public class FirstModel
{
[Required]
[MinLength(5)]
public string P1 = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(SecondValidator))]
public SecondModel? P2;
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModel P3;
}
public class SecondModel
{
[Required]
[MinLength(5)]
public string P4 = string.Empty;
}
public struct ThirdModel
{
[Required]
[MinLength(5)]
public string P5 = string.Empty;
public int P6 = default;
public ThirdModel(object _)
{
}
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
public void Validate()
{
}
public void Validate(int _)
{
}
public void Validate(string? _)
{
}
public void Validate(string? _0, object _1)
{
}
}
[OptionsValidator]
public partial struct SecondValidator : IValidateOptions<SecondModel>
{
}
}

View file

@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace FileScopedNamespace;
#pragma warning disable SA1649 // File name should match first type name
public class FirstModel
{
[Required]
[MinLength(5)]
public string P1 = string.Empty;
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
}

View file

@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace FunnyStrings
{
#pragma warning disable SA1649
#pragma warning disable SA1402
public class FirstModel
{
[RegularExpression("\"\r\n\\\\")]
public string P1 { get; set; } = string.Empty;
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
}
}

View file

@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace Generics
{
#pragma warning disable SA1649
#pragma warning disable SA1402
public class FirstModel<T>
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
public T? P2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public SecondModel? P3 { get; set; }
}
public class SecondModel
{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string.Empty;
}
[OptionsValidator]
public partial class FirstValidator<T> : IValidateOptions<FirstModel<T>>
{
}
}

View file

@ -0,0 +1,251 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
using Microsoft.Gen.OptionsValidation.Test;
#pragma warning disable SA1649
#pragma warning disable SA1402
namespace TestClasses.OptionsValidation
{
// ValidationAttribute without parameter
public class RequiredAttributeModel
{
[Required]
public string? Val { get; set; }
}
// ValidationAttribute with string parameter
public class RegularExpressionAttributeModel
{
[RegularExpression("\\s")]
public string Val { get; set; } = string.Empty;
}
// DataTypeAttribute
public class EmailAttributeModel
{
[EmailAddress]
public string Val { get; set; } = string.Empty;
}
// ValidationAttribute with System.Type parameter
public class CustomValidationAttributeModel
{
[CustomValidation(typeof(CustomValidationTest), "TestMethod")]
public string Val { get; set; } = string.Empty;
}
#pragma warning disable SA1204 // Static elements should appear before instance elements
public static class CustomValidationTest
#pragma warning restore SA1204 // Static elements should appear before instance elements
{
public static ValidationResult? TestMethod(string val, ValidationContext _)
{
if (val.Equals("Pass", StringComparison.Ordinal))
{
return ValidationResult.Success;
}
throw new ValidationException();
}
}
// ValidationAttribute with DataType parameter
public class DataTypeAttributeModel
{
[DataType(DataType.Text)]
public string Val { get; set; } = string.Empty;
}
// ValidationAttribute with type, double, int parameters
public class RangeAttributeModelInt
{
[Range(1, 3)]
public int Val { get; set; }
}
public class RangeAttributeModelDouble
{
[Range(0.5, 0.9)]
public double Val { get; set; }
}
public class RangeAttributeModelDate
{
#if NETCOREAPP3_1_OR_GREATER
[Range(typeof(DateTime), "1/2/2004", "3/4/2004", ParseLimitsInInvariantCulture = true)]
#else
[Range(typeof(DateTime), "1/2/2004", "3/4/2004")]
#endif
public DateTime Val { get; set; }
}
public class MultipleAttributeModel
{
[Required]
[DataType(DataType.Password)]
public string Val1 { get; set; } = string.Empty;
[Range(1, 3)]
public int Val2 { get; set; }
[Range(3, 5)]
public int Val3 { get; set; }
[Range(5, 9)]
public int Val4 { get; set; }
}
public class CustomTypeCustomValidationAttributeModel
{
[CustomValidation(typeof(CustomTypeCustomValidationTest), "TestMethod")]
public CustomType? Val { get; set; }
}
public class CustomType
{
public string Val1 { get; set; } = string.Empty;
public string Val2 { get; set; } = string.Empty;
}
#pragma warning disable SA1204 // Static elements should appear before instance elements
public static class CustomTypeCustomValidationTest
#pragma warning restore SA1204 // Static elements should appear before instance elements
{
public static ValidationResult? TestMethod(CustomType val, ValidationContext _)
{
if (val.Val1.Equals("Pass", StringComparison.Ordinal) && val.Val2.Equals("Pass", StringComparison.Ordinal))
{
return ValidationResult.Success;
}
throw new ValidationException();
}
}
public class AttributePropertyModel
{
[Range(1, 3, ErrorMessage = "ErrorMessage")]
public int Val1 { get; set; }
[Range(1, 3, ErrorMessageResourceType = typeof(SR), ErrorMessageResourceName = "ErrorMessageResourceName")]
public int Val2 { get; set; }
}
public class TypeWithoutOptionsValidator
{
[Required]
public string? Val1 { get; set; }
[Range(typeof(DateTime), "1/2/2004", "3/4/2004")]
public DateTime Val2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public RangeAttributeModelDouble? YetAnotherComplexVal { get; set; }
}
public class DerivedModel : RequiredAttributeModel
{
[Required]
public string? DerivedVal { get; set; }
[Required]
internal virtual int? VirtualValWithAttr { get; set; }
public virtual int? VirtualValWithoutAttr { get; set; }
[Required]
public new int? Val { get; set; }
}
public class LeafModel : DerivedModel
{
internal override int? VirtualValWithAttr { get; set; }
[Required]
public override int? VirtualValWithoutAttr { get; set; }
}
public class ComplexModel
{
[Microsoft.Extensions.Options.ValidateObjectMembers]
public RequiredAttributeModel? ComplexVal { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public TypeWithoutOptionsValidator? ValWithoutOptionsValidator { get; set; }
}
[OptionsValidator]
public partial class RequiredAttributeModelValidator : IValidateOptions<RequiredAttributeModel>
{
}
[OptionsValidator]
public partial class RegularExpressionAttributeModelValidator : IValidateOptions<RegularExpressionAttributeModel>
{
}
[OptionsValidator]
public partial class EmailAttributeModelValidator : IValidateOptions<EmailAttributeModel>
{
}
[OptionsValidator]
public partial class CustomValidationAttributeModelValidator : IValidateOptions<CustomValidationAttributeModel>
{
}
[OptionsValidator]
public partial class DataTypeAttributeModelValidator : IValidateOptions<DataTypeAttributeModel>
{
}
[OptionsValidator]
public partial class RangeAttributeModelIntValidator : IValidateOptions<RangeAttributeModelInt>
{
}
[OptionsValidator]
public partial class RangeAttributeModelDoubleValidator : IValidateOptions<RangeAttributeModelDouble>
{
}
[OptionsValidator]
public partial class RangeAttributeModelDateValidator : IValidateOptions<RangeAttributeModelDate>
{
}
[OptionsValidator]
public partial class MultipleAttributeModelValidator : IValidateOptions<MultipleAttributeModel>
{
}
[OptionsValidator]
public partial class CustomTypeCustomValidationAttributeModelValidator : IValidateOptions<CustomTypeCustomValidationAttributeModel>
{
}
[OptionsValidator]
public partial class AttributePropertyModelValidator : IValidateOptions<AttributePropertyModel>
{
}
[OptionsValidator]
public partial class DerivedModelValidator : IValidateOptions<DerivedModel>
{
}
[OptionsValidator]
public partial class LeafModelValidator : IValidateOptions<LeafModel>
{
}
[OptionsValidator]
internal sealed partial class ComplexModelValidator : IValidateOptions<ComplexModel>
{
}
}

View file

@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace MultiModelValidator
{
#pragma warning disable SA1649
#pragma warning disable SA1402
public class FirstModel
{
[Required]
[MinLength(5)]
public string P1 = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(MultiValidator))]
public SecondModel? P2;
}
public class SecondModel
{
[Required]
[MinLength(5)]
public string P3 = string.Empty;
}
[OptionsValidator]
public partial struct MultiValidator : IValidateOptions<FirstModel>, IValidateOptions<SecondModel>
{
}
}

View file

@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// #if ROSLYN_4_0_OR_GREATER
// #if ROSLYN4_0_OR_GREATER
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace Nested
{
#pragma warning disable SA1649
#pragma warning disable SA1402
public static class Container1
{
public class FirstModel
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(Container2.Container3.SecondValidator))]
public SecondModel? P2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModel P3 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(Container4.Container5.ThirdValidator))]
public SecondModel? P4 { get; set; }
}
public class SecondModel
{
[Required]
[MinLength(5)]
public string P5 { get; set; } = string.Empty;
}
public struct ThirdModel
{
public ThirdModel(int _)
{
}
[Required]
[MinLength(5)]
public string P6 { get; set; } = string.Empty;
}
}
public static partial class Container2
{
public partial class Container3
{
public Container3(int _)
{
// nothing to do
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<Container1.FirstModel>
{
}
[OptionsValidator]
public partial struct SecondValidator : IValidateOptions<Container1.SecondModel>
{
}
}
}
public partial record class Container4
{
public partial record class Container5
{
public Container5(int _)
{
// nothing to do
}
[OptionsValidator]
public partial struct ThirdValidator : IValidateOptions<Container1.SecondModel>
{
}
}
}
public partial struct Container6
{
[OptionsValidator]
public partial struct FourthValidator : IValidateOptions<Container1.SecondModel>
{
}
}
public partial record struct Container7
{
[OptionsValidator]
public partial record struct FifthValidator : IValidateOptions<Container1.SecondModel>
{
}
}
}
// #endif

View file

@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
#pragma warning disable SA1649
#pragma warning disable SA1402
public class FirstModelNoNamespace
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(SecondValidatorNoNamespace))]
public SecondModelNoNamespace? P2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModelNoNamespace? P3 { get; set; }
}
public class SecondModelNoNamespace
{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string.Empty;
}
public class ThirdModelNoNamespace
{
[Required]
[MinLength(5)]
public string P5 { get; set; } = string.Empty;
}
[OptionsValidator]
public partial class FirstValidatorNoNamespace : IValidateOptions<FirstModelNoNamespace>
{
}
[OptionsValidator]
public partial class SecondValidatorNoNamespace : IValidateOptions<SecondModelNoNamespace>
{
}

View file

@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace RandomMembers
{
#pragma warning disable SA1649
#pragma warning disable SA1402
#pragma warning disable CA1822
public class FirstModel
{
[Required]
[MinLength(5)]
public string? P1 { get; set; }
public void Foo()
{
throw new NotSupportedException();
}
public class Nested
{
}
}
[OptionsValidator]
public partial class FirstValidator : IValidateOptions<FirstModel>
{
}
}

View file

@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// #if ROSLYN_4_0_OR_GREATER
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace RecordTypes
{
#pragma warning disable SA1649
public record class FirstModel
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(SecondValidator))]
public SecondModel? P2 { get; set; }
[Microsoft.Extensions.Options.ValidateObjectMembers(typeof(ThirdValidator))]
public SecondModel P3 { get; set; } = new SecondModel();
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModel P4 { get; set; }
}
public record class SecondModel
{
[Required]
[MinLength(5)]
public string P5 { get; set; } = string.Empty;
}
public record struct ThirdModel
{
[Required]
[MinLength(5)]
public string P6 { get; set; } = string.Empty;
public ThirdModel(int _)
{
}
public ThirdModel(object _)
{
}
}
[OptionsValidator]
public partial record struct FirstValidator : IValidateOptions<FirstModel>
{
}
[OptionsValidator]
public partial record struct SecondValidator : IValidateOptions<SecondModel>
{
}
[OptionsValidator]
public partial record class ThirdValidator : IValidateOptions<SecondModel>
{
}
}
// #endif

View file

@ -0,0 +1,46 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace RepeatedTypes
{
#pragma warning disable SA1649
#pragma warning disable SA1402
#pragma warning disable CA1019
public class FirstModel
{
[Required]
[Microsoft.Extensions.Options.ValidateObjectMembers]
public SecondModel? P1 { get; set; }
[Required]
[Microsoft.Extensions.Options.ValidateObjectMembers]
public SecondModel? P2 { get; set; }
[Required]
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModel? P3 { get; set; }
}
public class SecondModel
{
[Required]
[Microsoft.Extensions.Options.ValidateObjectMembers]
public ThirdModel? P4 { get; set; }
}
public class ThirdModel
{
[Required]
[MinLength(5)]
public string? P5;
}
[OptionsValidator]
public partial class FirstValidator : IValidateOptions<FirstModel>
{
}
}

View file

@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace SelfValidation
{
#pragma warning disable SA1649
public class FirstModel : IValidatableObject
{
[Required]
public string P1 = string.Empty;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (P1.Length < 5)
{
return new[] { new ValidationResult("P1 is not long enough") };
}
return Array.Empty<ValidationResult>();
}
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
}
}

View file

@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Options;
namespace ValueTypes
{
#pragma warning disable SA1649
public class FirstModel
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
[ValidateObjectMembers]
public SecondModel? P2 { get; set; }
[ValidateObjectMembers]
public SecondModel P3 { get; set; }
[ValidateObjectMembers]
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1125:Use shorthand for nullable types", Justification = "Testing System>Nullable<T>")]
public Nullable<SecondModel> P4 { get; set; }
}
public struct SecondModel
{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string.Empty;
public SecondModel(object _)
{
}
}
[OptionsValidator]
public partial struct FirstValidator : IValidateOptions<FirstModel>
{
}
}