// 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();
}
}
}
}