| File: ModelBinding\Validation\ExplicitIndexCollectionValidationStrategy.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; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation; /// <summary> /// An implementation of <see cref="IValidationStrategy"/> for a collection bound using 'explicit indexing' /// style keys. /// </summary> /// <remarks> /// This implementation handles cases like: /// <example> /// Model: IList<Student> /// Query String: ?students.index=Joey,Katherine&students[Joey].Age=8&students[Katherine].Age=9 /// /// In this case, 'Joey' and 'Katherine' need to be used in the model prefix keys, but cannot be inferred /// form inspecting the collection. These prefixes are captured during model binding, and mapped to /// the corresponding ordinal index of a model object in the collection. The enumerator returned from this /// class will yield two 'Student' objects with corresponding keys 'students[Joey]' and 'students[Katherine]'. /// </example> /// /// Using this key format, the enumerator enumerates model objects of type matching /// <see cref="ModelMetadata.ElementMetadata"/>. The keys captured during model binding are mapped to the elements /// in the collection to compute the model prefix keys. /// </remarks> internal sealed class ExplicitIndexCollectionValidationStrategy : IValidationStrategy { /// <summary> /// Creates a new <see cref="ExplicitIndexCollectionValidationStrategy"/>. /// </summary> /// <param name="elementKeys">The keys of collection elements that were used during model binding.</param> public ExplicitIndexCollectionValidationStrategy(IEnumerable<string> elementKeys) { ArgumentNullException.ThrowIfNull(elementKeys); ElementKeys = elementKeys; } /// <summary> /// Gets the keys of collection elements that were used during model binding. /// </summary> public IEnumerable<string> ElementKeys { get; } /// <inheritdoc /> public IEnumerator<ValidationEntry> GetChildren( ModelMetadata metadata, string key, object model) { var enumerator = DefaultCollectionValidationStrategy.Instance.GetEnumeratorForElementType(metadata, model); return new Enumerator(metadata.ElementMetadata!, key, ElementKeys, enumerator); } private sealed class Enumerator : IEnumerator<ValidationEntry> { private readonly string _key; private readonly ModelMetadata _metadata; private readonly IEnumerator _enumerator; private readonly IEnumerator<string> _keyEnumerator; private ValidationEntry _entry; public Enumerator( ModelMetadata metadata, string key, IEnumerable<string> elementKeys, IEnumerator enumerator) { _metadata = metadata; _key = key; _keyEnumerator = elementKeys.GetEnumerator(); _enumerator = enumerator; } public ValidationEntry Current => _entry; object IEnumerator.Current => Current; public bool MoveNext() { if (!_keyEnumerator.MoveNext()) { return false; } if (!_enumerator.MoveNext()) { return false; } var model = _enumerator.Current; var key = ModelNames.CreateIndexModelName(_key, _keyEnumerator.Current); _entry = new ValidationEntry(_metadata, key, model); return true; } public void Dispose() { } public void Reset() { throw new NotImplementedException(); } } }