| File: ModelBinding\CompositeValueProvider.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.ObjectModel; namespace Microsoft.AspNetCore.Mvc.ModelBinding; /// <summary> /// Represents a <see cref="IValueProvider"/> whose values come from a collection of <see cref="IValueProvider"/>s. /// </summary> public class CompositeValueProvider : Collection<IValueProvider>, IEnumerableValueProvider, IBindingSourceValueProvider, IKeyRewriterValueProvider { /// <summary> /// Initializes a new instance of <see cref="CompositeValueProvider"/>. /// </summary> public CompositeValueProvider() { } /// <summary> /// Initializes a new instance of <see cref="CompositeValueProvider"/>. /// </summary> /// <param name="valueProviders">The sequence of <see cref="IValueProvider"/> to add to this instance of /// <see cref="CompositeValueProvider"/>.</param> public CompositeValueProvider(IList<IValueProvider> valueProviders) : base(valueProviders) { } /// <summary> /// Asynchronously creates a <see cref="CompositeValueProvider"/> using the provided /// <paramref name="controllerContext"/>. /// </summary> /// <param name="controllerContext">The <see cref="ControllerContext"/> associated with the current request.</param> /// <returns> /// A <see cref="Task{TResult}"/> which, when completed, asynchronously returns a /// <see cref="CompositeValueProvider"/>. /// </returns> public static async Task<CompositeValueProvider> CreateAsync(ControllerContext controllerContext) { ArgumentNullException.ThrowIfNull(controllerContext); var factories = controllerContext.ValueProviderFactories; return await CreateAsync(controllerContext, factories); } /// <summary> /// Asynchronously creates a <see cref="CompositeValueProvider"/> using the provided /// <paramref name="actionContext"/>. /// </summary> /// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param> /// <param name="factories">The <see cref="IValueProviderFactory"/> to be applied to the context.</param> /// <returns> /// A <see cref="Task{TResult}"/> which, when completed, asynchronously returns a /// <see cref="CompositeValueProvider"/>. /// </returns> public static async Task<CompositeValueProvider> CreateAsync( ActionContext actionContext, IList<IValueProviderFactory> factories) { var valueProviderFactoryContext = new ValueProviderFactoryContext(actionContext); for (var i = 0; i < factories.Count; i++) { var factory = factories[i]; await factory.CreateValueProviderAsync(valueProviderFactoryContext); } return new CompositeValueProvider(valueProviderFactoryContext.ValueProviders); } internal static async ValueTask<(bool success, CompositeValueProvider? valueProvider)> TryCreateAsync( ActionContext actionContext, IList<IValueProviderFactory> factories) { try { var valueProvider = await CreateAsync(actionContext, factories); return (true, valueProvider); } catch (ValueProviderException exception) { actionContext.ModelState.TryAddModelException(key: string.Empty, exception); return (false, null); } } /// <inheritdoc /> public virtual bool ContainsPrefix(string prefix) { for (var i = 0; i < Count; i++) { if (this[i].ContainsPrefix(prefix)) { return true; } } return false; } /// <inheritdoc /> public virtual ValueProviderResult GetValue(string key) { // Performance-sensitive // Caching the count is faster for IList<T> var itemCount = Items.Count; for (var i = 0; i < itemCount; i++) { var valueProvider = Items[i]; var result = valueProvider.GetValue(key); if (result != ValueProviderResult.None) { return result; } } return ValueProviderResult.None; } /// <inheritdoc /> public virtual IDictionary<string, string> GetKeysFromPrefix(string prefix) { foreach (var valueProvider in this) { if (valueProvider is IEnumerableValueProvider enumeratedProvider) { var result = enumeratedProvider.GetKeysFromPrefix(prefix); if (result != null && result.Count > 0) { return result; } } } return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } /// <inheritdoc /> protected override void InsertItem(int index, IValueProvider item) { ArgumentNullException.ThrowIfNull(item); base.InsertItem(index, item); } /// <inheritdoc /> protected override void SetItem(int index, IValueProvider item) { ArgumentNullException.ThrowIfNull(item); base.SetItem(index, item); } /// <inheritdoc /> public IValueProvider? Filter(BindingSource bindingSource) { ArgumentNullException.ThrowIfNull(bindingSource); var shouldFilter = false; for (var i = 0; i < Count; i++) { var valueProvider = Items[i]; if (valueProvider is IBindingSourceValueProvider) { shouldFilter = true; break; } } if (!shouldFilter) { // No inner IBindingSourceValueProvider implementations. Result will be empty. return null; } var filteredValueProviders = new List<IValueProvider>(); for (var i = 0; i < Count; i++) { var valueProvider = Items[i]; if (valueProvider is IBindingSourceValueProvider bindingSourceValueProvider) { var result = bindingSourceValueProvider.Filter(bindingSource); if (result != null) { filteredValueProviders.Add(result); } } } if (filteredValueProviders.Count == 0) { // Do not create an empty CompositeValueProvider. return null; } return new CompositeValueProvider(filteredValueProviders); } /// <inheritdoc /> /// <remarks> /// Value providers are included by default. If a contained <see cref="IValueProvider"/> does not implement /// <see cref="IKeyRewriterValueProvider"/>, <see cref="Filter()"/> will not remove it. /// </remarks> public IValueProvider? Filter() { var shouldFilter = false; for (var i = 0; i < Count; i++) { var valueProvider = Items[i]; if (valueProvider is IKeyRewriterValueProvider) { shouldFilter = true; break; } } if (!shouldFilter) { // No inner IKeyRewriterValueProvider implementations. Nothing to exclude. return this; } var filteredValueProviders = new List<IValueProvider>(); for (var i = 0; i < Count; i++) { var valueProvider = Items[i]; if (valueProvider is IKeyRewriterValueProvider keyRewriterValueProvider) { var result = keyRewriterValueProvider.Filter(); if (result != null) { filteredValueProviders.Add(result); } } else { // Assume value providers that aren't rewriter-aware do not rewrite their keys. filteredValueProviders.Add(valueProvider); } } if (filteredValueProviders.Count == 0) { // Do not create an empty CompositeValueProvider. return null; } return new CompositeValueProvider(filteredValueProviders); } }