// 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.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; namespace Microsoft.AspNetCore.Mvc.ModelBinding { /// /// Represents a whose values come from a collection of s. /// public class CompositeValueProvider : Collection, IEnumerableValueProvider, IBindingSourceValueProvider, IKeyRewriterValueProvider { /// /// Initializes a new instance of . /// public CompositeValueProvider() { } /// /// Initializes a new instance of . /// /// The sequence of to add to this instance of /// . public CompositeValueProvider(IList valueProviders) : base(valueProviders) { } /// /// Asynchronously creates a using the provided /// . /// /// The associated with the current request. /// /// A which, when completed, asynchronously returns a /// . /// public static async Task CreateAsync(ControllerContext controllerContext) { if (controllerContext == null) { throw new ArgumentNullException(nameof(controllerContext)); } var factories = controllerContext.ValueProviderFactories; return await CreateAsync(controllerContext, factories); } /// /// Asynchronously creates a using the provided /// . /// /// The associated with the current request. /// The to be applied to the context. /// /// A which, when completed, asynchronously returns a /// . /// public static async Task CreateAsync( ActionContext actionContext, IList 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); } /// public virtual bool ContainsPrefix(string prefix) { for (var i = 0; i < Count; i++) { if (this[i].ContainsPrefix(prefix)) { return true; } } return false; } /// public virtual ValueProviderResult GetValue(string key) { // Performance-sensitive // Caching the count is faster for IList 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; } /// public virtual IDictionary 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(StringComparer.OrdinalIgnoreCase); } /// protected override void InsertItem(int index, IValueProvider item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } base.InsertItem(index, item); } /// protected override void SetItem(int index, IValueProvider item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } base.SetItem(index, item); } /// public IValueProvider Filter(BindingSource bindingSource) { if (bindingSource == null) { throw new ArgumentNullException(nameof(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(); 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); } /// /// /// Value providers are included by default. If a contained does not implement /// , will not remove it. /// 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(); 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); } } }