// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; namespace Microsoft.AspNetCore.Mvc.Internal { /// /// An implementation of for a collection bound using 'explict indexing' /// style keys. /// /// /// This implemenation handles cases like: /// /// 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]'. /// /// /// Using this key format, the enumerator enumerates model objects of type matching /// . The keys captured during model binding are mapped to the elements /// in the collection to compute the model prefix keys. /// public class ExplicitIndexCollectionValidationStrategy : IValidationStrategy { /// /// Creates a new . /// /// The keys of collection elements that were used during model binding. public ExplicitIndexCollectionValidationStrategy(IEnumerable elementKeys) { if (elementKeys == null) { throw new ArgumentNullException(nameof(elementKeys)); } ElementKeys = elementKeys; } /// /// Gets the keys of collection elements that were used during model binding. /// public IEnumerable ElementKeys { get; } /// public IEnumerator GetChildren( ModelMetadata metadata, string key, object model) { var enumerator = DefaultCollectionValidationStrategy.Instance.GetEnumeratorForElementType(metadata, model); return new Enumerator(metadata.ElementMetadata, key, ElementKeys, enumerator); } private class Enumerator : IEnumerator { private readonly string _key; private readonly ModelMetadata _metadata; private readonly IEnumerator _enumerator; private readonly IEnumerator _keyEnumerator; private ValidationEntry _entry; public Enumerator( ModelMetadata metadata, string key, IEnumerable elementKeys, IEnumerator enumerator) { _metadata = metadata; _key = key; _keyEnumerator = elementKeys.GetEnumerator(); _enumerator = enumerator; } public ValidationEntry Current { get { return _entry; } } object IEnumerator.Current { get { return 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(); } } } }