// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.Framework.DependencyInjection; namespace Microsoft.AspNet.Mvc { /// /// This attribute can be used on action parameters and types, to indicate model level metadata. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] public class BindAttribute : Attribute, IModelNameProvider, IPropertyBindingPredicateProvider { private static readonly Func _defaultFilter = (context, propertyName) => true; private Func _predicateFromInclude; /// /// Creates a new instace of . /// /// Names of parameters to include in binding. public BindAttribute(params string[] include) { Include = include; } /// /// Creates a new instance of . /// /// The type which implements /// . /// public BindAttribute([NotNull] Type predicateProviderType) { if (!typeof(IPropertyBindingPredicateProvider).IsAssignableFrom(predicateProviderType)) { var message = Resources.FormatPropertyBindingPredicateProvider_WrongType( predicateProviderType.FullName, typeof(IPropertyBindingPredicateProvider).FullName); throw new ArgumentException(message, nameof(predicateProviderType)); } PredicateProviderType = predicateProviderType; } /// public Type PredicateProviderType { get; } /// /// Gets the names of properties to include in model binding. /// public string[] Include { get; } /// /// Allows a user to specify a particular prefix to match during model binding. /// // This property is exposed for back compat reasons. public string Prefix { get; set; } /// /// Represents the model name used during model binding. /// string IModelNameProvider.Name { get { return Prefix; } } /// public Func PropertyFilter { get { if (PredicateProviderType != null) { return CreatePredicateFromProviderType(PredicateProviderType); } else if (Include != null && Include.Length > 0) { if (_predicateFromInclude == null) { _predicateFromInclude = (context, propertyName) => Include.Contains(propertyName, StringComparer.Ordinal); } return _predicateFromInclude; } else { return _defaultFilter; } } } private static Func CreatePredicateFromProviderType( Type predicateProviderType) { // Holding state to avoid execessive creation of the provider. var initialized = false; Func predicate = null; return (ModelBindingContext context, string propertyName) => { if (!initialized) { var services = context.OperationBindingContext.HttpContext.RequestServices; var activator = services.GetService(); var provider = (IPropertyBindingPredicateProvider)activator.CreateInstance( services, predicateProviderType); initialized = true; predicate = provider.PropertyFilter ?? _defaultFilter; } return predicate(context, propertyName); }; } } }