Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Options validation source generator #87587

Merged
merged 13 commits into from
Jun 20, 2023
19 changes: 15 additions & 4 deletions src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ internal sealed class Emitter : EmitterBase
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(
Expand Down Expand Up @@ -159,6 +163,8 @@ private void GenModelSelfValidationIfNecessary(ValidatedModel modelToValidate)
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);");
OutLn($"var validationResults = new {StaticListType}<{StaticValidationResultType}>();");
OutLn($"var validationAttributes = new {StaticListType}<{StaticValidationAttributeType}>();");
OutLn();

foreach (var vm in modelToValidate.MembersToValidate)
Expand Down Expand Up @@ -187,18 +193,23 @@ private void GenModelSelfValidationIfNecessary(ValidatedModel modelToValidate)
OutCloseBrace();
}

private void GenMemberValidation(ValidatedMember vm,
ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict)
private void GenMemberValidation(ValidatedMember vm, ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict)
{
OutLn($"context.MemberName = \"{vm.Name}\";");
OutLn($"context.DisplayName = baseName + \"{vm.Name}\";");

foreach (var attr in vm.ValidationAttributes)
{
var staticValidationAttributeInstance = GetOrAddStaticValidationAttribute(ref staticValidationAttributesDict, attr);

OutLn($"builder.AddResult({StaticValidationAttributeHolderClassFQN}.{staticValidationAttributeInstance.FieldName}.GetValidationResult(options.{vm.Name}, context));");
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();
OutLn($"validationResults.Clear();");
OutLn($"validationAttributes.Clear();");
}

private StaticFieldInfo GetOrAddStaticValidationAttribute(ref Dictionary<string, StaticFieldInfo> staticValidationAttributesDict, ValidationAttributeInfo attr)
Expand Down