| File: ModelBinding\Validation\DefaultComplexObjectValidationStrategy.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System.Collections; using Microsoft.AspNetCore.Mvc.Core; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation; /// <summary> /// The default implementation of <see cref="IValidationStrategy"/> for a complex object. /// </summary> internal sealed class DefaultComplexObjectValidationStrategy : IValidationStrategy { /// <summary> /// Gets an instance of <see cref="DefaultComplexObjectValidationStrategy"/>. /// </summary> public static readonly IValidationStrategy Instance = new DefaultComplexObjectValidationStrategy(); private DefaultComplexObjectValidationStrategy() { } /// <inheritdoc /> public IEnumerator<ValidationEntry> GetChildren( ModelMetadata metadata, string key, object model) { return new Enumerator(metadata, key, model); } private sealed class Enumerator : IEnumerator<ValidationEntry> { private readonly string _key; private readonly object _model; private readonly int _count; private readonly ModelMetadata _modelMetadata; private readonly IReadOnlyList<ModelMetadata> _parameters; private readonly IReadOnlyList<ModelMetadata> _properties; private ValidationEntry _entry; private int _index; public Enumerator( ModelMetadata modelMetadata, string key, object model) { _modelMetadata = modelMetadata; _key = key; _model = model; if (_modelMetadata.BoundConstructor == null) { _parameters = Array.Empty<ModelMetadata>(); } else { _modelMetadata.ThrowIfRecordTypeHasValidationOnProperties(); _parameters = _modelMetadata.BoundConstructor.BoundConstructorParameters!; } _properties = _modelMetadata.BoundProperties; _count = _properties.Count + _parameters.Count; _index = -1; } public ValidationEntry Current => _entry; object IEnumerator.Current => Current; public bool MoveNext() { _index++; if (_index >= _count) { return false; } if (_index < _parameters.Count) { var parameter = _parameters[_index]; var parameterName = parameter.BinderModelName ?? parameter.ParameterName; var key = ModelNames.CreatePropertyModelName(_key, parameterName); if (_model is null) { _entry = new ValidationEntry(parameter, key, model: null); } else { if (!_modelMetadata.BoundConstructorParameterMapping.TryGetValue(parameter, out var property)) { throw new InvalidOperationException( Resources.FormatValidationStrategy_MappedPropertyNotFound(parameterName, _modelMetadata.ModelType)); } _entry = new ValidationEntry(parameter, key, () => GetModel(_model, property)); } } else { var property = _properties[_index - _parameters.Count]; var propertyName = property.ValidationModelName ?? property.BinderModelName ?? property.PropertyName; var key = ModelNames.CreatePropertyModelName(_key, propertyName); if (_model == null) { // Performance: Never create a delegate when container is null. _entry = new ValidationEntry(property, key, model: null); } else { _entry = new ValidationEntry(property, key, () => GetModel(_model, property)); } } return true; } public void Dispose() { } public void Reset() { throw new NotImplementedException(); } private static object? GetModel(object container, ModelMetadata property) { return property.PropertyGetter!(container); } } }