Adding support for bind attribute.
This commit is contained in:
parent
c0d8ca8aed
commit
75405e3b76
|
|
@ -16,14 +16,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// </summary>
|
||||
public class DefaultControllerActionArgumentBinder : IControllerActionArgumentBinder
|
||||
{
|
||||
private readonly IBodyModelValidator _modelValidator;
|
||||
private readonly IActionBindingContextProvider _bindingContextProvider;
|
||||
|
||||
public DefaultControllerActionArgumentBinder(IActionBindingContextProvider bindingContextProvider,
|
||||
IBodyModelValidator modelValidator)
|
||||
public DefaultControllerActionArgumentBinder(IActionBindingContextProvider bindingContextProvider)
|
||||
{
|
||||
_bindingContextProvider = bindingContextProvider;
|
||||
_modelValidator = modelValidator;
|
||||
}
|
||||
|
||||
public async Task<IDictionary<string, object>> GetActionArgumentsAsync(ActionContext actionContext)
|
||||
|
|
@ -49,8 +46,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
|
||||
private async Task PopulateActionArgumentsAsync(IEnumerable<ModelMetadata> modelMetadatas,
|
||||
ActionBindingContext actionBindingContext,
|
||||
IDictionary<string, object> invocationInfo)
|
||||
ActionBindingContext actionBindingContext,
|
||||
IDictionary<string, object> invocationInfo)
|
||||
{
|
||||
var bodyBoundParameterCount = modelMetadatas.Count(
|
||||
modelMetadata => modelMetadata.Marker is IBodyBinderMarker);
|
||||
|
|
@ -61,19 +58,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
foreach (var modelMetadata in modelMetadatas)
|
||||
{
|
||||
var parameterType = modelMetadata.ModelType;
|
||||
var modelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelName = modelMetadata.PropertyName,
|
||||
ModelMetadata = modelMetadata,
|
||||
ModelState = actionBindingContext.ActionContext.ModelState,
|
||||
ModelBinder = actionBindingContext.ModelBinder,
|
||||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
FallbackToEmptyPrefix = true,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
};
|
||||
var modelBindingContext = GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
|
||||
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
||||
{
|
||||
|
|
@ -81,5 +66,25 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static ModelBindingContext GetModelBindingContext(ModelMetadata modelMetadata, ActionBindingContext actionBindingContext)
|
||||
{
|
||||
var modelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelName = modelMetadata.ModelName ?? modelMetadata.PropertyName,
|
||||
ModelMetadata = modelMetadata,
|
||||
ModelState = actionBindingContext.ActionContext.ModelState,
|
||||
ModelBinder = actionBindingContext.ModelBinder,
|
||||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
|
||||
// Fallback only if there is no explicit model name set.
|
||||
FallbackToEmptyPrefix = modelMetadata.ModelName == null,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
};
|
||||
|
||||
return modelBindingContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute can be used on action parameters and types, to indicate model level metadata.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class BindAttribute : Attribute, IModelNameProvider, IModelPropertyBindingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Comma separated set of properties which are to be excluded during model binding.
|
||||
/// </summary>
|
||||
public string Exclude { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Comma separated set of properties which are to be included during model binding.
|
||||
/// </summary>
|
||||
public string Include { get; set; } = string.Empty;
|
||||
|
||||
// This property is exposed for back compat reasons.
|
||||
/// <summary>
|
||||
/// Allows a user to specify a particular prefix to match during model binding.
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents the model name used during model binding.
|
||||
/// </summary>
|
||||
string IModelNameProvider.Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return Prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
ModelBindingHelper.ValidateBindingContext(bindingContext);
|
||||
|
||||
if (!CanBindType(bindingContext.ModelType) ||
|
||||
!await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName))
|
||||
if (!CanBindType(bindingContext.ModelType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var topLevelObject = bindingContext.ModelMetadata.ContainerType == null;
|
||||
var isThereAnExplicitAlias = bindingContext.ModelMetadata.ModelName != null;
|
||||
|
||||
|
||||
// The first check is necessary because if we fallback to empty prefix, we do not want to depend
|
||||
// on a value provider to provide a value for empty prefix.
|
||||
var containsPrefix = (bindingContext.ModelName == string.Empty && topLevelObject) ||
|
||||
await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName);
|
||||
|
||||
// Always create the model if
|
||||
// 1. It is a top level object and the model name is empty.
|
||||
// 2. There is a value provider which can provide value for the model name.
|
||||
// 3. There is an explicit alias provided by the user and it is a top level object.
|
||||
// The reson we depend on explicit alias is that otherwise we want the FallToEmptyPrefix codepath
|
||||
// to kick in so that empty prefix values could be bound.
|
||||
if (!containsPrefix && !(isThereAnExplicitAlias && topLevelObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -149,6 +168,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var validationInfo = GetPropertyValidationInfo(bindingContext);
|
||||
return bindingContext.ModelMetadata.Properties
|
||||
.Where(propertyMetadata =>
|
||||
IsPropertyAllowed(propertyMetadata.PropertyName,
|
||||
bindingContext.ModelMetadata.IncludedProperties,
|
||||
bindingContext.ModelMetadata.ExcludedProperties) &&
|
||||
(validationInfo.RequiredProperties.Contains(propertyMetadata.PropertyName) ||
|
||||
!validationInfo.SkipProperties.Contains(propertyMetadata.PropertyName)) &&
|
||||
CanUpdateProperty(propertyMetadata));
|
||||
|
|
@ -349,6 +371,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return addedError;
|
||||
}
|
||||
|
||||
private 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 exclude list implies no properties are disallowed.
|
||||
var includeProperty = (includeProperties != null) &&
|
||||
includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var excludeProperty = (excludeProperties != null) &&
|
||||
excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return includeProperty && !excludeProperty;
|
||||
}
|
||||
|
||||
internal sealed class PropertyValidationInfo
|
||||
{
|
||||
public PropertyValidationInfo()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entity which can provide model name as metadata.
|
||||
/// </summary>
|
||||
public interface IModelNameProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Model name.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entity which has binding information for a model.
|
||||
/// </summary>
|
||||
public interface IModelPropertyBindingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Comma separated set of properties which are to be excluded during model binding.
|
||||
/// </summary>
|
||||
string Exclude { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Comma separated set of properties which are to be included during model binding.
|
||||
/// </summary>
|
||||
string Include { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Override for applying the prototype + modelAccess to yield the final metadata
|
||||
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype,
|
||||
Func<object> modelAccessor);
|
||||
|
||||
private ModelMetadata GetMetadataForParameterCore(Func<object> modelAccessor,
|
||||
string parameterName,
|
||||
ParameterInfo parameter)
|
||||
|
|
@ -94,10 +93,61 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
CreateParameterInfo(parameter.ParameterType,
|
||||
parameter.GetCustomAttributes(),
|
||||
parameterName);
|
||||
var typePrototype = GetTypeInformation(parameter.ParameterType).Prototype;
|
||||
|
||||
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);
|
||||
|
|
@ -162,8 +212,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
properties.Add(propertyHelper.Name, CreatePropertyInformation(type, propertyHelper));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
new CachedDataAnnotationsMetadataAttributes(attributes))
|
||||
{
|
||||
Marker = attributes.OfType<IBinderMarker>().FirstOrDefault();
|
||||
|
||||
var modelNameProvider = attributes.OfType<IModelNameProvider>().FirstOrDefault();
|
||||
ModelName = modelNameProvider?.Name;
|
||||
|
||||
var bindAttribute = attributes.OfType<BindAttribute>().FirstOrDefault();
|
||||
ReadSettingsFromBindAttribute(bindAttribute);
|
||||
}
|
||||
|
||||
protected override bool ComputeConvertEmptyStringToNull()
|
||||
|
|
@ -265,5 +271,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
modelType.FullName, displayColumnAttribute.DisplayColumn));
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadSettingsFromBindAttribute(BindAttribute bindAttribute)
|
||||
{
|
||||
if (bindAttribute == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ExcludedProperties = SplitString(bindAttribute.Exclude).ToList();
|
||||
IncludedProperties = SplitString(bindAttribute.Include).ToList();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitString(string original)
|
||||
{
|
||||
if (string.IsNullOrEmpty(original))
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
var split = original.Split(',')
|
||||
.Select(piece => piece.Trim())
|
||||
.Where(trimmed => !string.IsNullOrEmpty(trimmed));
|
||||
return split;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
CacheKey = prototype.CacheKey;
|
||||
PrototypeCache = prototype.PrototypeCache;
|
||||
Marker = prototype.Marker;
|
||||
IncludedProperties = prototype.IncludedProperties;
|
||||
ExcludedProperties = prototype.ExcludedProperties;
|
||||
ModelName = prototype.ModelName;
|
||||
_isComplexType = prototype.IsComplexType;
|
||||
_isComplexTypeComputed = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
_modelAccessor = modelAccessor;
|
||||
_modelType = modelType;
|
||||
_propertyName = propertyName;
|
||||
|
||||
_convertEmptyStringToNull = true;
|
||||
_isRequired = !modelType.AllowsNullValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the name of a model if specified explicitly using <see cref="IModelNameProvider"/>.
|
||||
/// </summary>
|
||||
public string ModelName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Properties which are marked as Included for this model.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> IncludedProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Properties which are marked as Excluded for this model.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> ExcludedProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a binder marker for this model.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1402,8 +1402,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
actionDescriptor,
|
||||
inputFormattersProvider.Object,
|
||||
new DefaultControllerActionArgumentBinder(
|
||||
actionBindingContextProvider.Object,
|
||||
new DefaultBodyModelValidator()));
|
||||
actionBindingContextProvider.Object));
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,103 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
{
|
||||
public class ControllerActionArgumentBinderTests
|
||||
{
|
||||
public class MySimpleModel
|
||||
{
|
||||
}
|
||||
|
||||
[Bind(Prefix = "TypePrefix")]
|
||||
public class MySimpleModelWithTypeBasedBind
|
||||
{
|
||||
}
|
||||
|
||||
public void ParameterHasFieldPrefix([Bind(Prefix = "bar")] string foo)
|
||||
{
|
||||
}
|
||||
|
||||
public void ParameterHasEmptyFieldPrefix([Bind(Prefix = "")] MySimpleModel foo,
|
||||
[Bind(Prefix = "")] MySimpleModelWithTypeBasedBind foo1)
|
||||
{
|
||||
}
|
||||
|
||||
public void ParameterHasPrefixAndComplexType([Bind(Prefix = "bar")] MySimpleModel foo,
|
||||
[Bind(Prefix = "bar")] MySimpleModelWithTypeBasedBind foo1)
|
||||
{
|
||||
}
|
||||
|
||||
public void ParameterHasEmptyBindAttribute([Bind] MySimpleModel foo,
|
||||
[Bind] MySimpleModelWithTypeBasedBind foo1)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ParameterHasFieldPrefix", false, "bar")]
|
||||
[InlineData("ParameterHasEmptyFieldPrefix", false, "")]
|
||||
[InlineData("ParameterHasPrefixAndComplexType", false, "bar")]
|
||||
[InlineData("ParameterHasEmptyBindAttribute", true, "foo")]
|
||||
public void GetModelBindingContext_ModelBindingContextIsSetWithModelName_ForParameters(
|
||||
string actionMethodName, bool expectedFallToEmptyPrefix, string expectedModelName)
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ControllerActionArgumentBinderTests);
|
||||
var methodInfo = type.GetMethod(actionMethodName);
|
||||
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||
Mock.Of<ActionDescriptor>());
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
||||
methodInfo: methodInfo,
|
||||
parameterName: "foo");
|
||||
|
||||
|
||||
var actionBindingContext = new ActionBindingContext(actionContext,
|
||||
Mock.Of<IModelMetadataProvider>(),
|
||||
Mock.Of<IModelBinder>(),
|
||||
Mock.Of<IValueProvider>(),
|
||||
Mock.Of<IInputFormatterSelector>(),
|
||||
Mock.Of<IModelValidatorProvider>());
|
||||
// Act
|
||||
var context = DefaultControllerActionArgumentBinder
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedFallToEmptyPrefix, context.FallbackToEmptyPrefix);
|
||||
Assert.Equal(expectedModelName, context.ModelName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ParameterHasEmptyFieldPrefix", false, "")]
|
||||
[InlineData("ParameterHasPrefixAndComplexType", false, "bar")]
|
||||
[InlineData("ParameterHasEmptyBindAttribute", true, "foo1")]
|
||||
public void GetModelBindingContext_ModelBindingContextIsNotSet_ForTypes(
|
||||
string actionMethodName, bool expectedFallToEmptyPrefix, string expectedModelName)
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ControllerActionArgumentBinderTests);
|
||||
var methodInfo = type.GetMethod(actionMethodName);
|
||||
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||
Mock.Of<ActionDescriptor>());
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
||||
methodInfo: methodInfo,
|
||||
parameterName: "foo1");
|
||||
|
||||
|
||||
var actionBindingContext = new ActionBindingContext(actionContext,
|
||||
Mock.Of<IModelMetadataProvider>(),
|
||||
Mock.Of<IModelBinder>(),
|
||||
Mock.Of<IValueProvider>(),
|
||||
Mock.Of<IInputFormatterSelector>(),
|
||||
Mock.Of<IModelValidatorProvider>());
|
||||
// Act
|
||||
var context = DefaultControllerActionArgumentBinder
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedFallToEmptyPrefix, context.FallbackToEmptyPrefix);
|
||||
Assert.Equal(expectedModelName, context.ModelName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parameters_WithMultipleFromBody_Throw()
|
||||
{
|
||||
|
|
@ -53,7 +150,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
.Returns(Task.FromResult(bindingContext));
|
||||
|
||||
var invoker = new DefaultControllerActionArgumentBinder(
|
||||
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||
actionBindingContextProvider.Object);
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
|
|
@ -101,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
.Returns(Task.FromResult(bindingContext));
|
||||
|
||||
var invoker = new DefaultControllerActionArgumentBinder(
|
||||
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||
actionBindingContextProvider.Object);
|
||||
|
||||
// Act
|
||||
var result = await invoker.GetActionArgumentsAsync(actionContext);
|
||||
|
|
@ -153,7 +250,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
.Returns(Task.FromResult(bindingContext));
|
||||
|
||||
var invoker = new DefaultControllerActionArgumentBinder(
|
||||
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||
actionBindingContextProvider.Object);
|
||||
|
||||
// Act
|
||||
var result = await invoker.GetActionArgumentsAsync(actionContext);
|
||||
|
|
|
|||
|
|
@ -235,5 +235,126 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal("The Field2 field is required.", json["model.Field2"]);
|
||||
Assert.Equal("The Field3 field is required.", json["model.Field3"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_AppliesAtBothParameterAndTypeLevelTogether_BlacklistedAtEitherLevelIsNotBound()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_BlackListingAtEitherLevelDoesNotBind" +
|
||||
"?param1.IncludedExplicitlyAtTypeLevel=someValue¶m2.ExcludedExplicitlyAtTypeLevel=someValue");
|
||||
|
||||
// Assert
|
||||
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
|
||||
Assert.Equal(2, json.Count);
|
||||
Assert.Null(json["param1.IncludedExplicitlyAtTypeLevel"]);
|
||||
Assert.Null(json["param2.ExcludedExplicitlyAtTypeLevel"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_AppliesAtBothParameterAndTypeLevelTogether_WhitelistedAtBothLevelsIsBound()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_WhiteListingAtBothLevelBinds" +
|
||||
"?param1.IncludedExplicitlyAtTypeLevel=someValue¶m2.ExcludedExplicitlyAtTypeLevel=someValue");
|
||||
|
||||
// Assert
|
||||
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
|
||||
Assert.Equal(1, json.Count);
|
||||
Assert.Equal("someValue", json["param1.IncludedExplicitlyAtTypeLevel"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_AppliesAtBothParameterAndTypeLevelTogether_WhitelistingAtOneLevelIsNotBound()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_WhiteListingAtOnlyOneLevelDoesNotBind" +
|
||||
"?param1.IncludedExplicitlyAtTypeLevel=someValue¶m1.IncludedExplicitlyAtParameterLevel=someValue");
|
||||
|
||||
// Assert
|
||||
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
|
||||
Assert.Equal(2, json.Count);
|
||||
Assert.Null(json["param1.IncludedExplicitlyAtParameterLevel"]);
|
||||
Assert.Null(json["param1.IncludedExplicitlyAtTypeLevel"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_BindsUsingParameterPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"BindParameterUsingParameterPrefix" +
|
||||
"?randomPrefix.Value=someValue");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("someValue", response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_DoesNotUseTypePrefix()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"TypePrefixIsNeverUsed" +
|
||||
"?param.Value=someValue");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("someValue", response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_FallsBackOnEmptyPrefixIfNoParameterPrefixIsProvided()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetStringAsync("http://localhost/BindAttribute/" +
|
||||
"TypePrefixIsNeverUsed" +
|
||||
"?Value=someValue");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("someValue", response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindAttribute_DoesNotFallBackOnEmptyPrefixIfParameterPrefixIsProvided()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("http://localhost/BindAttribute/" +
|
||||
"BindParameterUsingParameterPrefix" +
|
||||
"?Value=someValue");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||
Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
{
|
||||
public class BindAttributeTest
|
||||
{
|
||||
[Fact]
|
||||
public void PrefixPropertyDefaultsToNull()
|
||||
{
|
||||
// Arrange
|
||||
BindAttribute attr = new BindAttribute();
|
||||
|
||||
// Act & assert
|
||||
Assert.Null(attr.Prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Moq;
|
||||
|
|
@ -58,6 +59,48 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
testableBinder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindModel_InitsInstance_ForEmptyModelName()
|
||||
{
|
||||
// Arrange
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var mockDtoBinder = new Mock<IModelBinder>();
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForObject(new Person()),
|
||||
ModelName = "",
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
ModelBinder = mockDtoBinder.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
};
|
||||
|
||||
mockDtoBinder
|
||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
{
|
||||
// just return the DTO unchanged
|
||||
return Task.FromResult(true);
|
||||
});
|
||||
|
||||
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
|
||||
testableBinder.Setup(o => o.EnsureModelPublic(bindingContext)).Verifiable();
|
||||
testableBinder.Setup(o => o.GetMetadataForPropertiesPublic(bindingContext))
|
||||
.Returns(new ModelMetadata[0]).Verifiable();
|
||||
|
||||
// Act
|
||||
var retValue = await testableBinder.Object.BindModelAsync(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(retValue);
|
||||
Assert.IsType<Person>(bindingContext.Model);
|
||||
Assert.True(bindingContext.ValidationNode.ValidateAllProperties);
|
||||
testableBinder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanUpdateProperty_HasPublicSetter_ReturnsTrue()
|
||||
{
|
||||
|
|
@ -194,7 +237,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
|
@ -215,7 +259,75 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(Person)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedPropertyNames, returnedPropertyNames);
|
||||
}
|
||||
|
||||
[Bind(Exclude = nameof(Excluded1) + "," + nameof(Excluded2))]
|
||||
private class TypeWithExcludedPropertiesUsingBindAttribute
|
||||
{
|
||||
public int Excluded1 { get; set; }
|
||||
|
||||
public int Excluded2 { get; set; }
|
||||
|
||||
public int IncludedByDefault1 { get; set; }
|
||||
public int IncludedByDefault2 { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForProperties_DoesNotReturn_ExcludedProperties()
|
||||
{
|
||||
// Arrange
|
||||
var expectedPropertyNames = new[] { "IncludedByDefault1", "IncludedByDefault2" };
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(TypeWithExcludedPropertiesUsingBindAttribute)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedPropertyNames, returnedPropertyNames);
|
||||
}
|
||||
|
||||
[Bind(Include = nameof(IncludedExplicitly1) + "," + nameof(IncludedExplicitly2))]
|
||||
private class TypeWithIncludedPropertiesUsingBindAttribute
|
||||
{
|
||||
public int ExcludedByDefault1 { get; set; }
|
||||
|
||||
public int ExcludedByDefault2 { get; set; }
|
||||
|
||||
public int IncludedExplicitly1 { get; set; }
|
||||
|
||||
public int IncludedExplicitly2 { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForProperties_ReturnsOnlyWhiteListedProperties_UsingBindAttributeInclude()
|
||||
{
|
||||
// Arrange
|
||||
var expectedPropertyNames = new[] { "IncludedExplicitly1", "IncludedExplicitly2" };
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(TypeWithIncludedPropertiesUsingBindAttribute)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
|
@ -812,6 +924,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return metadataProvider.GetMetadataForType(null, t);
|
||||
}
|
||||
|
||||
private static ModelMetadata GetMetadataForParameter(MethodInfo methodInfo, string parameterName)
|
||||
{
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
return metadataProvider.GetMetadataForParameter(null, methodInfo, parameterName);
|
||||
}
|
||||
|
||||
private class Person
|
||||
{
|
||||
private DateTime? _dateOfDeath;
|
||||
|
|
|
|||
|
|
@ -3,12 +3,117 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class CachedDataAnnotationsModelMetadataProviderTest
|
||||
{
|
||||
[Bind(Include = nameof(IncludedAndExcludedExplicitly1) + "," + nameof(IncludedExplicitly1),
|
||||
Exclude = nameof(IncludedAndExcludedExplicitly1) + "," + nameof(ExcludedExplicitly1),
|
||||
Prefix = "TypePrefix")]
|
||||
private class TypeWithExludedAndIncludedPropertiesUsingBindAttribute
|
||||
{
|
||||
public int ExcludedExplicitly1 { get; set; }
|
||||
|
||||
public int IncludedAndExcludedExplicitly1 { get; set; }
|
||||
|
||||
public int IncludedExplicitly1 { get; set; }
|
||||
|
||||
public int NotIncludedOrExcluded { get; set; }
|
||||
|
||||
public void ActionWithBindAttribute(
|
||||
[Bind(Include = "Property1, Property2,IncludedAndExcludedExplicitly1",
|
||||
Exclude ="Property3, Property4, IncludedAndExcludedExplicitly1",
|
||||
Prefix = "ParameterPrefix")]
|
||||
TypeWithExludedAndIncludedPropertiesUsingBindAttribute param)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsIncludedAndExcludedProperties_ForTypes()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
var expectedIncludedPropertyNames = new[] { "IncludedAndExcludedExplicitly1", "IncludedExplicitly1" };
|
||||
var expectedExcludedPropertyNames = new[] { "IncludedAndExcludedExplicitly1", "ExcludedExplicitly1" };
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForType(null, type);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedIncludedPropertyNames.ToList(), metadata.IncludedProperties);
|
||||
Assert.Equal(expectedExcludedPropertyNames.ToList(), metadata.ExcludedProperties);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ModelMetadataProvider_ReadsIncludedAndExcludedProperties_BothAtParameterAndTypeLevel_ForParameters()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
var methodInfo = type.GetMethod("ActionWithBindAttribute");
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Note it does an intersection for included and a union for excluded.
|
||||
var expectedIncludedPropertyNames = new[] { "IncludedAndExcludedExplicitly1" };
|
||||
var expectedExcludedPropertyNames = new[] {
|
||||
"Property3", "Property4", "IncludedAndExcludedExplicitly1", "ExcludedExplicitly1" };
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedIncludedPropertyNames.ToList(), metadata.IncludedProperties);
|
||||
Assert.Equal(expectedExcludedPropertyNames.ToList(), metadata.ExcludedProperties);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ModelMetadataProvider_ReadsPrefixProperty_OnlyAtParameterLevel_ForParameters()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
var methodInfo = type.GetMethod("ActionWithBindAttribute");
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsModelNameProperty_ForTypes()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForType(null, type);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("TypePrefix", metadata.ModelName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsModelNameProperty_ForParameters()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TypeWithExludedAndIncludedPropertiesUsingBindAttribute);
|
||||
var methodInfo = type.GetMethod("ActionWithBindAttribute");
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act
|
||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForDisplay()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
public class BindAttributeController : Controller
|
||||
{
|
||||
public Dictionary<string, string>
|
||||
BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_BlackListingAtEitherLevelDoesNotBind(
|
||||
[Bind(Exclude = "IncludedExplicitlyAtTypeLevel")] TypeWithIncludedPropertyAtBindAttribute param1,
|
||||
[Bind(Include = "ExcludedExplicitlyAtTypeLevel")] TypeWithExcludedPropertyAtBindAttribute param2)
|
||||
{
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
// The first one should not be included because the parameter level bind attribute filters it out.
|
||||
{ "param1.IncludedExplicitlyAtTypeLevel", param1.IncludedExplicitlyAtTypeLevel },
|
||||
|
||||
// The second one should not be included because the type level bind attribute filters it out.
|
||||
{ "param2.ExcludedExplicitlyAtTypeLevel", param2.ExcludedExplicitlyAtTypeLevel },
|
||||
};
|
||||
}
|
||||
|
||||
public Dictionary<string, string>
|
||||
BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_WhiteListingAtBothLevelBinds(
|
||||
[Bind(Include = "IncludedExplicitlyAtTypeLevel")] TypeWithIncludedPropertyAtBindAttribute param1)
|
||||
{
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
// The since this is included at both level it is bound.
|
||||
{ "param1.IncludedExplicitlyAtTypeLevel", param1.IncludedExplicitlyAtTypeLevel },
|
||||
};
|
||||
}
|
||||
|
||||
public Dictionary<string, string>
|
||||
BindAtParamterLevelAndBindAtTypeLevelAreBothEvaluated_WhiteListingAtOnlyOneLevelDoesNotBind(
|
||||
[Bind(Include = "IncludedExplicitlyAtParameterLevel")]
|
||||
TypeWithIncludedPropertyAtParameterAndTypeUsingBindAttribute param1)
|
||||
{
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
// The since this is included at only type level it is not bound.
|
||||
{ "param1.IncludedExplicitlyAtParameterLevel", param1.IncludedExplicitlyAtParameterLevel },
|
||||
{ "param1.IncludedExplicitlyAtTypeLevel", param1.IncludedExplicitlyAtTypeLevel },
|
||||
};
|
||||
}
|
||||
|
||||
public string BindParameterUsingParameterPrefix([Bind(Prefix = "randomPrefix")] ParameterPrefix param)
|
||||
{
|
||||
return param.Value;
|
||||
}
|
||||
|
||||
// This will use param to try to bind and not the value specified at TypePrefix.
|
||||
public string TypePrefixIsNeverUsed([Bind] TypePrefix param)
|
||||
{
|
||||
return param.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Bind(Prefix = "TypePrefix")]
|
||||
public class TypePrefix
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class ParameterPrefix
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
[Bind(Include = nameof(IncludedExplicitlyAtTypeLevel))]
|
||||
public class TypeWithIncludedPropertyAtParameterAndTypeUsingBindAttribute
|
||||
{
|
||||
public string IncludedExplicitlyAtTypeLevel { get; set; }
|
||||
public string IncludedExplicitlyAtParameterLevel { get; set; }
|
||||
}
|
||||
|
||||
[Bind(Include = nameof(IncludedExplicitlyAtTypeLevel))]
|
||||
public class TypeWithIncludedPropertyAtBindAttribute
|
||||
{
|
||||
public string IncludedExplicitlyAtTypeLevel { get; set; }
|
||||
}
|
||||
|
||||
[Bind(Exclude = nameof(ExcludedExplicitlyAtTypeLevel))]
|
||||
public class TypeWithExcludedPropertyAtBindAttribute
|
||||
{
|
||||
public string ExcludedExplicitlyAtTypeLevel { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue