// 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; using System.Reflection; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; namespace Microsoft.AspNetCore.Mvc.Internal { /// /// A default implementation of . /// public class DefaultBindingMetadataProvider : IBindingMetadataProvider { public void CreateBindingMetadata(BindingMetadataProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } // BinderModelName foreach (var binderModelNameAttribute in context.Attributes.OfType()) { if (binderModelNameAttribute?.Name != null) { context.BindingMetadata.BinderModelName = binderModelNameAttribute.Name; break; } } // BinderType foreach (var binderTypeAttribute in context.Attributes.OfType()) { if (binderTypeAttribute.BinderType != null) { context.BindingMetadata.BinderType = binderTypeAttribute.BinderType; break; } } // BindingSource foreach (var bindingSourceAttribute in context.Attributes.OfType()) { if (bindingSourceAttribute.BindingSource != null) { context.BindingMetadata.BindingSource = bindingSourceAttribute.BindingSource; break; } } // PropertyFilterProvider var propertyFilterProviders = context.Attributes.OfType().ToArray(); if (propertyFilterProviders.Length == 0) { context.BindingMetadata.PropertyFilterProvider = null; } else if (propertyFilterProviders.Length == 1) { context.BindingMetadata.PropertyFilterProvider = propertyFilterProviders[0]; } else { var composite = new CompositePropertyFilterProvider(propertyFilterProviders); context.BindingMetadata.PropertyFilterProvider = composite; } if (context.Key.MetadataKind == ModelMetadataKind.Property) { // BindingBehavior can fall back to attributes on the Container Type, but we should ignore // attributes on the Property Type. var bindingBehavior = context.PropertyAttributes.OfType().FirstOrDefault(); if (bindingBehavior == null) { bindingBehavior = context.Key.ContainerType.GetTypeInfo() .GetCustomAttributes(typeof(BindingBehaviorAttribute), inherit: true) .OfType() .FirstOrDefault(); } if (bindingBehavior != null) { context.BindingMetadata.IsBindingAllowed = bindingBehavior.Behavior != BindingBehavior.Never; context.BindingMetadata.IsBindingRequired = bindingBehavior.Behavior == BindingBehavior.Required; } } } private class CompositePropertyFilterProvider : IPropertyFilterProvider { private readonly IEnumerable _providers; public CompositePropertyFilterProvider(IEnumerable providers) { _providers = providers; } public Func PropertyFilter { get { return 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; }; } } } }