Adding PropertyFilter instead of depending on model metadata to concatenate Metadata for parameter/property and type metadata
This commit is contained in:
parent
77c4391e47
commit
d1c0213a10
|
|
@ -85,6 +85,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
internal static ModelBindingContext GetModelBindingContext(ModelMetadata modelMetadata, ActionBindingContext actionBindingContext)
|
||||
{
|
||||
Predicate<string> propertyFilter =
|
||||
propertyName => BindAttribute.IsPropertyAllowed(propertyName,
|
||||
modelMetadata.IncludedProperties,
|
||||
modelMetadata.ExcludedProperties);
|
||||
|
||||
var modelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelName = modelMetadata.ModelName ?? modelMetadata.PropertyName,
|
||||
|
|
@ -94,7 +99,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
|
||||
PropertyFilter = propertyFilter,
|
||||
// Fallback only if there is no explicit model name set.
|
||||
FallbackToEmptyPrefix = modelMetadata.ModelName == null,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// 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.AspNet.Mvc
|
||||
{
|
||||
|
|
@ -37,5 +39,21 @@ namespace Microsoft.AspNet.Mvc
|
|||
return Prefix;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPropertyAllowed(string propertyName,
|
||||
IReadOnlyList<string> includeProperties,
|
||||
IReadOnlyList<string> excludeProperties)
|
||||
{
|
||||
// We allow a property to be bound if its both in the include list AND not in the exclude list.
|
||||
// An empty include list implies all properties are allowed.
|
||||
// An empty exclude list implies no properties are disallowed.
|
||||
var includeProperty = (includeProperties == null) ||
|
||||
(includeProperties.Count == 0) ||
|
||||
includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
|
||||
var excludeProperty = (excludeProperties != null) &&
|
||||
excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return includeProperty && !excludeProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
MetadataProvider = oldBindingContext.MetadataProvider,
|
||||
ModelBinder = oldBindingContext.ModelBinder,
|
||||
HttpContext = oldBindingContext.HttpContext,
|
||||
PropertyFilter = oldBindingContext.PropertyFilter,
|
||||
};
|
||||
|
||||
// validation is expensive to create, so copy it over if we can
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
EnsureModel(bindingContext);
|
||||
var propertyMetadatas = GetMetadataForProperties(bindingContext);
|
||||
var propertyMetadatas = GetMetadataForProperties(bindingContext).ToArray();
|
||||
var dto = CreateAndPopulateDto(bindingContext, propertyMetadatas);
|
||||
|
||||
// post-processing, e.g. property setters and hooking up validation
|
||||
|
|
@ -166,11 +166,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
protected virtual IEnumerable<ModelMetadata> GetMetadataForProperties(ModelBindingContext bindingContext)
|
||||
{
|
||||
var validationInfo = GetPropertyValidationInfo(bindingContext);
|
||||
var propertyTypeMetadata = bindingContext.MetadataProvider
|
||||
.GetMetadataForType(null, bindingContext.ModelType);
|
||||
Predicate<string> newPropertyFilter =
|
||||
propertyName => bindingContext.PropertyFilter(propertyName) &&
|
||||
BindAttribute.IsPropertyAllowed(
|
||||
propertyName,
|
||||
propertyTypeMetadata.IncludedProperties,
|
||||
propertyTypeMetadata.ExcludedProperties);
|
||||
|
||||
return bindingContext.ModelMetadata.Properties
|
||||
.Where(propertyMetadata =>
|
||||
IsPropertyAllowed(propertyMetadata.PropertyName,
|
||||
bindingContext.ModelMetadata.IncludedProperties,
|
||||
bindingContext.ModelMetadata.ExcludedProperties) &&
|
||||
newPropertyFilter(propertyMetadata.PropertyName) &&
|
||||
(validationInfo.RequiredProperties.Contains(propertyMetadata.PropertyName) ||
|
||||
!validationInfo.SkipProperties.Contains(propertyMetadata.PropertyName)) &&
|
||||
CanUpdateProperty(propertyMetadata));
|
||||
|
|
|
|||
|
|
@ -82,61 +82,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
parameter.GetCustomAttributes(),
|
||||
parameterName,
|
||||
binderMarker);
|
||||
|
||||
var typeInfo = GetTypeInformation(parameter.ParameterType);
|
||||
UpdateMetadataWithTypeInfo(parameterInfo.Prototype, typeInfo);
|
||||
|
||||
return CreateMetadataFromPrototype(parameterInfo.Prototype, modelAccessor);
|
||||
}
|
||||
|
||||
private void UpdateMetadataWithTypeInfo(ModelMetadata parameterPrototype, TypeInformation typeInfo)
|
||||
{
|
||||
// If both are empty
|
||||
// Include everything.
|
||||
// If none are empty
|
||||
// Include common.
|
||||
// If nothing common
|
||||
// Dont include anything.
|
||||
if (typeInfo.Prototype.IncludedProperties == null || typeInfo.Prototype.IncludedProperties.Count == 0)
|
||||
{
|
||||
if (parameterPrototype.IncludedProperties == null || parameterPrototype.IncludedProperties.Count == 0)
|
||||
{
|
||||
parameterPrototype.IncludedProperties = typeInfo.Properties
|
||||
.Select(property => property.Key)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parameterPrototype.IncludedProperties == null || parameterPrototype.IncludedProperties.Count == 0)
|
||||
{
|
||||
parameterPrototype.IncludedProperties = typeInfo.Prototype.IncludedProperties;
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterPrototype.IncludedProperties = parameterPrototype.IncludedProperties
|
||||
.Intersect(typeInfo.Prototype.IncludedProperties,
|
||||
StringComparer.OrdinalIgnoreCase).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeInfo.Prototype.ExcludedProperties != null)
|
||||
{
|
||||
if (parameterPrototype.ExcludedProperties == null || parameterPrototype.ExcludedProperties.Count == 0)
|
||||
{
|
||||
parameterPrototype.ExcludedProperties = typeInfo.Prototype.ExcludedProperties;
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterPrototype.ExcludedProperties = parameterPrototype.ExcludedProperties
|
||||
.Union(typeInfo.Prototype.ExcludedProperties,
|
||||
StringComparer.OrdinalIgnoreCase).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore the ModelName specified at Type level. (This is to be compatible with MVC).
|
||||
}
|
||||
|
||||
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
|
||||
{
|
||||
var typeInfo = GetTypeInformation(containerType);
|
||||
|
|
@ -203,19 +151,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
info.Properties = properties;
|
||||
|
||||
if (info.Prototype != null)
|
||||
{
|
||||
// Update the included properties so that the properties are not ignored while binding.
|
||||
if (info.Prototype.IncludedProperties == null ||
|
||||
info.Prototype.IncludedProperties.Count == 0)
|
||||
{
|
||||
// Mark all properties as included.
|
||||
info.Prototype.IncludedProperties =
|
||||
info.Properties.Select(property => property.Key).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public class ModelBindingContext
|
||||
{
|
||||
private static readonly Predicate<string> _defaultPropertyFilter = _ => true;
|
||||
private string _modelName;
|
||||
private ModelStateDictionary _modelState;
|
||||
private Dictionary<string, ModelMetadata> _propertyMetadata;
|
||||
private ModelValidationNode _validationNode;
|
||||
private Predicate<string> _propertyFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBindingContext"/> class.
|
||||
|
|
@ -172,6 +174,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
public Predicate<string> PropertyFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_propertyFilter == null)
|
||||
{
|
||||
_propertyFilter = _defaultPropertyFilter;
|
||||
}
|
||||
return _propertyFilter;
|
||||
}
|
||||
set { _propertyFilter = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ModelValidationNode"/> instance used as a container for
|
||||
/// validation information.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void ModelMetadataProvider_ReadsIncludedAndExcludedProperties_BothAtParameterAndTypeLevel_ForParameters()
|
||||
public void ModelMetadataProvider_ReadsIncludedAndExcludedProperties_OnlyAtParameterLevel_ForParameters()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
|
|
@ -58,9 +58,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Note it does an intersection for included and a union for excluded.
|
||||
var expectedIncludedPropertyNames = new[] { "IncludedAndExcludedExplicitly1" };
|
||||
var expectedIncludedPropertyNames = new[] { "Property1", "Property2", "IncludedAndExcludedExplicitly1" };
|
||||
var expectedExcludedPropertyNames = new[] {
|
||||
"Property3", "Property4", "IncludedAndExcludedExplicitly1", "ExcludedExplicitly1" };
|
||||
"Property3", "Property4", "IncludedAndExcludedExplicitly1" };
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForParameter(
|
||||
|
|
|
|||
Loading…
Reference in New Issue