// 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.Linq; namespace Microsoft.AspNetCore.Mvc.ModelBinding { /// /// Binding info which represents metadata associated to an action parameter. /// public class BindingInfo { /// /// Creates a new . /// public BindingInfo() { } /// /// Creates a copy of a . /// /// The to copy. public BindingInfo(BindingInfo other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } BindingSource = other.BindingSource; BinderModelName = other.BinderModelName; BinderType = other.BinderType; PropertyFilterProvider = other.PropertyFilterProvider; RequestPredicate = other.RequestPredicate; } /// /// Gets or sets the . /// public BindingSource BindingSource { get; set; } /// /// Gets or sets the binder model name. /// public string BinderModelName { get; set; } /// /// Gets or sets the of the model binder used to bind the model. /// public Type BinderType { get; set; } /// /// Gets or sets the . /// public IPropertyFilterProvider PropertyFilterProvider { get; set; } /// /// Gets or sets a predicate which determines whether or not the model should be bound based on state /// from the current request. /// public Func RequestPredicate { get; set; } /// /// Constructs a new instance of from the given . /// /// This overload does not account for specified via . Consider using /// overload, or /// on the result of this method to to get a more accurate instance. /// /// /// A collection of attributes which are used to construct /// /// A new instance of . public static BindingInfo GetBindingInfo(IEnumerable attributes) { var bindingInfo = new BindingInfo(); var isBindingInfoPresent = false; // BinderModelName foreach (var binderModelNameAttribute in attributes.OfType()) { isBindingInfoPresent = true; if (binderModelNameAttribute?.Name != null) { bindingInfo.BinderModelName = binderModelNameAttribute.Name; break; } } // BinderType foreach (var binderTypeAttribute in attributes.OfType()) { isBindingInfoPresent = true; if (binderTypeAttribute.BinderType != null) { bindingInfo.BinderType = binderTypeAttribute.BinderType; break; } } // BindingSource foreach (var bindingSourceAttribute in attributes.OfType()) { isBindingInfoPresent = true; if (bindingSourceAttribute.BindingSource != null) { bindingInfo.BindingSource = bindingSourceAttribute.BindingSource; break; } } // PropertyFilterProvider var propertyFilterProviders = attributes.OfType().ToArray(); if (propertyFilterProviders.Length == 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = propertyFilterProviders[0]; } else if (propertyFilterProviders.Length > 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = new CompositePropertyFilterProvider(propertyFilterProviders); } // RequestPredicate foreach (var requestPredicateProvider in attributes.OfType()) { isBindingInfoPresent = true; if (requestPredicateProvider.RequestPredicate != null) { bindingInfo.RequestPredicate = requestPredicateProvider.RequestPredicate; break; } } return isBindingInfoPresent ? bindingInfo : null; } /// /// Constructs a new instance of from the given and . /// /// A collection of attributes which are used to construct . /// The . /// A new instance of if any binding metadata was discovered; otherwise or . public static BindingInfo GetBindingInfo(IEnumerable attributes, ModelMetadata modelMetadata) { if (attributes == null) { throw new ArgumentNullException(nameof(attributes)); } if (modelMetadata == null) { throw new ArgumentNullException(nameof(modelMetadata)); } var bindingInfo = GetBindingInfo(attributes); var isBindingInfoPresent = bindingInfo != null; if (bindingInfo == null) { bindingInfo = new BindingInfo(); } isBindingInfoPresent |= bindingInfo.TryApplyBindingInfo(modelMetadata); return isBindingInfoPresent ? bindingInfo : null; } /// /// Applies binding metadata from the specified . /// /// Uses values from if no value is already available. /// /// /// The . /// if any binding metadata from was applied; /// otherwise. public bool TryApplyBindingInfo(ModelMetadata modelMetadata) { if (modelMetadata == null) { throw new ArgumentNullException(nameof(modelMetadata)); } var isBindingInfoPresent = false; if (BinderModelName == null && modelMetadata.BinderModelName != null) { isBindingInfoPresent = true; BinderModelName = modelMetadata.BinderModelName; } if (BinderType == null && modelMetadata.BinderType != null) { isBindingInfoPresent = true; BinderType = modelMetadata.BinderType; } if (BindingSource == null && modelMetadata.BindingSource != null) { isBindingInfoPresent = true; BindingSource = modelMetadata.BindingSource; } if (PropertyFilterProvider == null && modelMetadata.PropertyFilterProvider != null) { isBindingInfoPresent = true; PropertyFilterProvider = modelMetadata.PropertyFilterProvider; } return isBindingInfoPresent; } private class CompositePropertyFilterProvider : IPropertyFilterProvider { private readonly IEnumerable _providers; public CompositePropertyFilterProvider(IEnumerable providers) { _providers = providers; } public Func PropertyFilter => CreatePropertyFilter(); private Func CreatePropertyFilter() { var propertyFilters = _providers .Select(p => p.PropertyFilter) .Where(p => p != null); return (m) => { foreach (var propertyFilter in propertyFilters) { if (!propertyFilter(m)) { return false; } } return true; }; } } } }