aspnetcore/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultBindingMetadataProvi...

138 lines
5.1 KiB
C#

// 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
{
/// <summary>
/// A default implementation of <see cref="IBindingMetadataProvider"/>.
/// </summary>
public class DefaultBindingMetadataProvider : IBindingMetadataProvider
{
private readonly ModelBindingMessageProvider _messageProvider;
public DefaultBindingMetadataProvider(ModelBindingMessageProvider messageProvider)
{
if (messageProvider == null)
{
throw new ArgumentNullException(nameof(messageProvider));
}
_messageProvider = messageProvider;
}
/// <inheritdoc />
public void GetBindingMetadata(BindingMetadataProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// BinderModelName
foreach (var binderModelNameAttribute in context.Attributes.OfType<IModelNameProvider>())
{
if (binderModelNameAttribute?.Name != null)
{
context.BindingMetadata.BinderModelName = binderModelNameAttribute.Name;
break;
}
}
// BinderType
foreach (var binderTypeAttribute in context.Attributes.OfType<IBinderTypeProviderMetadata>())
{
if (binderTypeAttribute.BinderType != null)
{
context.BindingMetadata.BinderType = binderTypeAttribute.BinderType;
break;
}
}
// BindingSource
foreach (var bindingSourceAttribute in context.Attributes.OfType<IBindingSourceMetadata>())
{
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<IPropertyBindingPredicateProvider>().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<BindingBehaviorAttribute>().FirstOrDefault();
if (bindingBehavior == null)
{
bindingBehavior =
context.Key.ContainerType.GetTypeInfo()
.GetCustomAttributes(typeof(BindingBehaviorAttribute), inherit: true)
.OfType<BindingBehaviorAttribute>()
.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<IPropertyBindingPredicateProvider> _providers;
public CompositePredicateProvider(IEnumerable<IPropertyBindingPredicateProvider> providers)
{
_providers = providers;
}
public Func<ModelBindingContext, string, bool> PropertyFilter
{
get
{
return CreatePredicate();
}
}
private Func<ModelBindingContext, string, bool> 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;
};
}
}
}
}