// 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; namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata { /// /// A default implementation of . /// public class DefaultBindingMetadataProvider : IBindingMetadataProvider { private readonly ModelBindingMessageProvider _messageProvider; public DefaultBindingMetadataProvider(ModelBindingMessageProvider messageProvider) { if (messageProvider == null) { throw new ArgumentNullException(nameof(messageProvider)); } _messageProvider = messageProvider; } /// public void GetBindingMetadata(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; } } // ModelBindingMessageProvider // Provide a unique instance based on one passed to the constructor. context.BindingMetadata.ModelBindingMessageProvider = new ModelBindingMessageProvider(_messageProvider); // PropertyBindingPredicateProvider var predicateProviders = context.Attributes.OfType().ToArray(); if (predicateProviders.Length > 0) { context.BindingMetadata.PropertyBindingPredicateProvider = new CompositePredicateProvider( predicateProviders); } 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 CompositePredicateProvider : IPropertyBindingPredicateProvider { private readonly IEnumerable _providers; public CompositePredicateProvider(IEnumerable providers) { _providers = providers; } public Func PropertyFilter { get { return CreatePredicate(); } } private Func CreatePredicate() { var predicates = _providers .Select(p => p.PropertyFilter) .Where(p => p != null); return (context, propertyName) => { foreach (var predicate in predicates) { if (!predicate(context, propertyName)) { return false; } } return true; }; } } } }