File: ModelBinding\Binders\ArrayModelBinder.cs
Web Access
Project: src\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.Diagnostics;
using System.Linq;
using Microsoft.Extensions.Logging;
 
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
 
/// <summary>
/// <see cref="IModelBinder"/> implementation for binding array values.
/// </summary>
/// <typeparam name="TElement">Type of elements in the array.</typeparam>
public class ArrayModelBinder<TElement> : CollectionModelBinder<TElement>
{
    /// <summary>
    /// Creates a new <see cref="ArrayModelBinder{TElement}"/>.
    /// </summary>
    /// <param name="elementBinder">
    /// The <see cref="IModelBinder"/> for binding <typeparamref name="TElement"/>.
    /// </param>
    /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
    public ArrayModelBinder(IModelBinder elementBinder, ILoggerFactory loggerFactory)
        : base(elementBinder, loggerFactory)
    {
    }
 
    /// <summary>
    /// Creates a new <see cref="ArrayModelBinder{TElement}"/>.
    /// </summary>
    /// <param name="elementBinder">
    /// The <see cref="IModelBinder"/> for binding <typeparamref name="TElement"/>.
    /// </param>
    /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
    /// <param name="allowValidatingTopLevelNodes">
    /// Indication that validation of top-level models is enabled. If <see langword="true"/> and
    /// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder
    /// adds a <see cref="ModelStateDictionary"/> error when the model is not bound.
    /// </param>
    /// <remarks>
    /// The <paramref name="allowValidatingTopLevelNodes"/> parameter is currently ignored.
    /// <see cref="CollectionModelBinder{TElement}.AllowValidatingTopLevelNodes"/> is always <see langword="true"/>
    /// in <see cref="ArrayModelBinder{TElement}"/>.
    /// </remarks>
    public ArrayModelBinder(
        IModelBinder elementBinder,
        ILoggerFactory loggerFactory,
        bool allowValidatingTopLevelNodes)
        : base(elementBinder, loggerFactory, allowValidatingTopLevelNodes: true)
    {
    }
 
    /// <summary>
    /// Creates a new <see cref="ArrayModelBinder{TElement}"/>.
    /// </summary>
    /// <param name="elementBinder">
    /// The <see cref="IModelBinder"/> for binding <typeparamref name="TElement"/>.
    /// </param>
    /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
    /// <param name="allowValidatingTopLevelNodes">
    /// Indication that validation of top-level models is enabled. If <see langword="true"/> and
    /// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder
    /// adds a <see cref="ModelStateDictionary"/> error when the model is not bound.
    /// </param>
    /// <param name="mvcOptions">The <see cref="MvcOptions"/>.</param>
    /// <remarks>
    /// <para>This is the preferred <see cref="ArrayModelBinder{TElement}"/> constructor.</para>
    /// <para>
    /// The <paramref name="allowValidatingTopLevelNodes"/> parameter is currently ignored.
    /// <see cref="CollectionModelBinder{TElement}.AllowValidatingTopLevelNodes"/> is always <see langword="true"/>
    /// in <see cref="ArrayModelBinder{TElement}"/>.
    /// </para>
    /// </remarks>
    public ArrayModelBinder(
        IModelBinder elementBinder,
        ILoggerFactory loggerFactory,
        bool allowValidatingTopLevelNodes,
        MvcOptions mvcOptions)
        : base(elementBinder, loggerFactory, allowValidatingTopLevelNodes: true, mvcOptions)
    {
    }
 
    /// <inheritdoc />
    public override bool CanCreateInstance(Type targetType)
    {
        return targetType == typeof(TElement[]);
    }
 
    /// <inheritdoc />
    protected override object CreateEmptyCollection(Type targetType)
    {
        Debug.Assert(targetType == typeof(TElement[]), "GenericModelBinder only creates this binder for arrays.");
 
        return Array.Empty<TElement>();
    }
 
    /// <inheritdoc />
    protected override object? ConvertToCollectionType(Type targetType, IEnumerable<TElement?> collection)
    {
        Debug.Assert(targetType == typeof(TElement?[]), "GenericModelBinder only creates this binder for arrays.");
 
        // If non-null, collection is a List<TElement>, never already a TElement[].
        return collection?.ToArray();
    }
 
    /// <inheritdoc />
    protected override void CopyToModel(object target, IEnumerable<TElement?> sourceCollection)
    {
        ArgumentNullException.ThrowIfNull(target);
 
        // Do not attempt to copy values into an array because an array's length is immutable. This choice is also
        // consistent with our handling of a read-only array property.
    }
}