132 lines
4.5 KiB
C#
132 lines
4.5 KiB
C#
// 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
|
|
{
|
|
/// <summary>
|
|
/// An implementation of <see cref="IValidationStrategy"/> for a collection bound using 'explict indexing'
|
|
/// style keys.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This implemenation 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>
|
|
public 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)
|
|
{
|
|
if (elementKeys == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(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 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
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|