Cleaning up StyleCop errors

This commit is contained in:
Pranav K 2014-06-04 12:59:06 -07:00
parent b96da245cb
commit 97e06138ed
39 changed files with 427 additions and 341 deletions

View File

@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.BindModelAsync(bindingContext); return base.BindModelAsync(bindingContext);
} }
protected override bool CreateOrReplaceCollection(ModelBindingContext bindingContext, IList<TElement> newCollection) protected override bool CreateOrReplaceCollection(ModelBindingContext bindingContext,
IList<TElement> newCollection)
{ {
bindingContext.Model = newCollection.ToArray(); bindingContext.Model = newCollection.ToArray();
return true; return true;

View File

@ -23,38 +23,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName);
var boundCollection = await ((valueProviderResult != null) ? var bindCollectionTask = valueProviderResult != null ?
BindSimpleCollection(bindingContext, valueProviderResult.RawValue, valueProviderResult.Culture) : BindSimpleCollection(bindingContext, valueProviderResult.RawValue, valueProviderResult.Culture) :
BindComplexCollection(bindingContext)); BindComplexCollection(bindingContext);
var boundCollection = await bindCollectionTask;
return CreateOrReplaceCollection(bindingContext, boundCollection); return CreateOrReplaceCollection(bindingContext, boundCollection);
} }
// TODO: Make this method internal
// Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value
// is [ "1", "2" ] and needs to be converted to an int[]. // is [ "1", "2" ] and needs to be converted to an int[].
public async Task<List<TElement>> BindSimpleCollection(ModelBindingContext bindingContext, internal async Task<List<TElement>> BindSimpleCollection(ModelBindingContext bindingContext,
object rawValue, object rawValue,
CultureInfo culture) CultureInfo culture)
{ {
if (rawValue == null) if (rawValue == null)
{ {
return null; // nothing to do return null; // nothing to do
} }
List<TElement> boundCollection = new List<TElement>(); var boundCollection = new List<TElement>();
object[] rawValueArray = RawValueToObjectArray(rawValue); var rawValueArray = RawValueToObjectArray(rawValue);
foreach (object rawValueElement in rawValueArray) foreach (var rawValueElement in rawValueArray)
{ {
ModelBindingContext innerBindingContext = new ModelBindingContext(bindingContext) var innerBindingContext = new ModelBindingContext(bindingContext)
{ {
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)), ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)),
ModelName = bindingContext.ModelName, ModelName = bindingContext.ModelName,
ValueProvider = new CompositeValueProvider ValueProvider = new CompositeValueProvider
{ {
// our temporary provider goes at the front of the list // our temporary provider goes at the front of the list
new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture), new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture),
bindingContext.ValueProvider bindingContext.ValueProvider
} }
}; };
@ -74,15 +74,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Used when the ValueProvider contains the collection to be bound as multiple elements, e.g. foo[0], foo[1]. // Used when the ValueProvider contains the collection to be bound as multiple elements, e.g. foo[0], foo[1].
private async Task<List<TElement>> BindComplexCollection(ModelBindingContext bindingContext) private async Task<List<TElement>> BindComplexCollection(ModelBindingContext bindingContext)
{ {
string indexPropertyName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName, "index"); var indexPropertyName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName, "index");
ValueProviderResult valueProviderResultIndex = await bindingContext.ValueProvider.GetValueAsync(indexPropertyName); var valueProviderResultIndex = await bindingContext.ValueProvider.GetValueAsync(indexPropertyName);
IEnumerable<string> indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(valueProviderResultIndex); var indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(valueProviderResultIndex);
return await BindComplexCollectionFromIndexes(bindingContext, indexNames); return await BindComplexCollectionFromIndexes(bindingContext, indexNames);
} }
// TODO: Convert to internal internal async Task<List<TElement>> BindComplexCollectionFromIndexes(ModelBindingContext bindingContext,
public async Task<List<TElement>> BindComplexCollectionFromIndexes(ModelBindingContext bindingContext, IEnumerable<string> indexNames)
IEnumerable<string> indexNames)
{ {
bool indexNamesIsFinite; bool indexNamesIsFinite;
if (indexNames != null) if (indexNames != null)
@ -96,20 +95,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Select(i => i.ToString(CultureInfo.InvariantCulture)); .Select(i => i.ToString(CultureInfo.InvariantCulture));
} }
List<TElement> boundCollection = new List<TElement>(); var boundCollection = new List<TElement>();
foreach (string indexName in indexNames) foreach (var indexName in indexNames)
{ {
string fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName); var fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName);
var childBindingContext = new ModelBindingContext(bindingContext) var childBindingContext = new ModelBindingContext(bindingContext)
{ {
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)), ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)),
ModelName = fullChildName ModelName = fullChildName
}; };
bool didBind = false; var didBind = false;
object boundValue = null; object boundValue = null;
Type modelType = bindingContext.ModelType; var modelType = bindingContext.ModelType;
if (await bindingContext.ModelBinder.BindModelAsync(childBindingContext)) if (await bindingContext.ModelBinder.BindModelAsync(childBindingContext))
{ {
@ -134,7 +133,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Extensibility point that allows the bound collection to be manipulated or transformed before // Extensibility point that allows the bound collection to be manipulated or transformed before
// being returned from the binder. // being returned from the binder.
protected virtual bool CreateOrReplaceCollection(ModelBindingContext bindingContext, IList<TElement> newCollection) protected virtual bool CreateOrReplaceCollection(ModelBindingContext bindingContext,
IList<TElement> newCollection)
{ {
CreateOrReplaceCollection(bindingContext, newCollection, () => new List<TElement>()); CreateOrReplaceCollection(bindingContext, newCollection, () => new List<TElement>());
return true; return true;
@ -150,13 +150,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new[] { rawValue }; return new[] { rawValue };
} }
object[] rawValueAsObjectArray = rawValue as object[]; var rawValueAsObjectArray = rawValue as object[];
if (rawValueAsObjectArray != null) if (rawValueAsObjectArray != null)
{ {
return rawValueAsObjectArray; return rawValueAsObjectArray;
} }
IEnumerable rawValueAsEnumerable = rawValue as IEnumerable; var rawValueAsEnumerable = rawValue as IEnumerable;
if (rawValueAsEnumerable != null) if (rawValueAsEnumerable != null)
{ {
return rawValueAsEnumerable.Cast<object>().ToArray(); return rawValueAsEnumerable.Cast<object>().ToArray();
@ -170,7 +170,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
IEnumerable<TElement> incomingElements, IEnumerable<TElement> incomingElements,
Func<ICollection<TElement>> creator) Func<ICollection<TElement>> creator)
{ {
ICollection<TElement> collection = bindingContext.Model as ICollection<TElement>; var collection = bindingContext.Model as ICollection<TElement>;
if (collection == null || collection.IsReadOnly) if (collection == null || collection.IsReadOnly)
{ {
collection = creator(); collection = creator();
@ -178,7 +178,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
collection.Clear(); collection.Clear();
foreach (TElement element in incomingElements) foreach (var element in incomingElements)
{ {
collection.Add(element); collection.Add(element);
} }

View File

@ -12,22 +12,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
if (bindingContext.ModelType == typeof(ComplexModelDto)) if (bindingContext.ModelType == typeof(ComplexModelDto))
{ {
ModelBindingHelper.ValidateBindingContext(bindingContext, typeof(ComplexModelDto), allowNullModel: false); ModelBindingHelper.ValidateBindingContext(bindingContext,
typeof(ComplexModelDto),
allowNullModel: false);
ComplexModelDto dto = (ComplexModelDto)bindingContext.Model; var dto = (ComplexModelDto)bindingContext.Model;
foreach (ModelMetadata propertyMetadata in dto.PropertyMetadata) foreach (var propertyMetadata in dto.PropertyMetadata)
{ {
ModelBindingContext propertyBindingContext = new ModelBindingContext(bindingContext) var propertyBindingContext = new ModelBindingContext(bindingContext)
{ {
ModelMetadata = propertyMetadata, ModelMetadata = propertyMetadata,
ModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName, propertyMetadata.PropertyName) ModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
propertyMetadata.PropertyName)
}; };
// bind and propagate the values // bind and propagate the values
// If we can't bind, then leave the result missing (don't add a null). // If we can't bind, then leave the result missing (don't add a null).
if (await bindingContext.ModelBinder.BindModelAsync(propertyBindingContext)) if (await bindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
{ {
dto.Results[propertyMetadata] = new ComplexModelDtoResult(propertyBindingContext.Model, propertyBindingContext.ValidationNode); var result = new ComplexModelDtoResult(propertyBindingContext.Model,
propertyBindingContext.ValidationNode);
dto.Results[propertyMetadata] = result;
} }
} }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </remarks> /// </remarks>
public class CompositeModelBinder : IModelBinder public class CompositeModelBinder : IModelBinder
{ {
public CompositeModelBinder(IEnumerable<IModelBinder> binders) public CompositeModelBinder([NotNull] IEnumerable<IModelBinder> binders)
: this(binders.ToArray()) : this(binders.ToArray())
{ {
} }
@ -31,8 +31,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bindingContext) public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bindingContext)
{ {
var newBindingContext = CreateNewBindingContext(bindingContext, var newBindingContext = CreateNewBindingContext(bindingContext,
bindingContext.ModelName, bindingContext.ModelName,
reuseValidationNode: true); reuseValidationNode: true);
var boundSuccessfully = await TryBind(newBindingContext); var boundSuccessfully = await TryBind(newBindingContext);
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
&& bindingContext.FallbackToEmptyPrefix) && bindingContext.FallbackToEmptyPrefix)
{ {
// fallback to empty prefix? // fallback to empty prefix?
newBindingContext = CreateNewBindingContext(bindingContext, newBindingContext = CreateNewBindingContext(bindingContext,
modelName: string.Empty, modelName: string.Empty,
reuseValidationNode: false); reuseValidationNode: false);
boundSuccessfully = await TryBind(newBindingContext); boundSuccessfully = await TryBind(newBindingContext);
@ -53,16 +53,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph. // Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph.
// Ignore ComplexModelDto since it essentially wraps the primary object. // Ignore ComplexModelDto since it essentially wraps the primary object.
if (newBindingContext.ModelMetadata.ContainerType == null && if (newBindingContext.ModelMetadata.ContainerType == null &&
newBindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto)) newBindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto))
{ {
// run validation and return the model // run validation and return the model
// If we fell back to an empty prefix above and are dealing with simple types, // If we fell back to an empty prefix above and are dealing with simple types,
// propagate the non-blank model name through for user clarity in validation errors. // propagate the non-blank model name through for user clarity in validation errors.
// Complex types will reveal their individual properties as model names and do not require this. // Complex types will reveal their individual properties as model names and do not require this.
if (!newBindingContext.ModelMetadata.IsComplexType && string.IsNullOrEmpty(newBindingContext.ModelName)) if (!newBindingContext.ModelMetadata.IsComplexType &&
string.IsNullOrEmpty(newBindingContext.ModelName))
{ {
newBindingContext.ValidationNode = new ModelValidationNode(newBindingContext.ModelMetadata, newBindingContext.ValidationNode = new ModelValidationNode(newBindingContext.ModelMetadata,
bindingContext.ModelName); bindingContext.ModelName);
} }
@ -74,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
newBindingContext.ValidationNode.Validate(validationContext, parentNode: null); newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
} }
bindingContext.Model = newBindingContext.Model; bindingContext.Model = newBindingContext.Model;
return true; return true;
} }
@ -97,7 +98,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return false; return false;
} }
private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext, private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext,
string modelName, string modelName,
bool reuseValidationNode) bool reuseValidationNode)
{ {
@ -110,7 +111,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ValidatorProviders = oldBindingContext.ValidatorProviders, ValidatorProviders = oldBindingContext.ValidatorProviders,
MetadataProvider = oldBindingContext.MetadataProvider, MetadataProvider = oldBindingContext.MetadataProvider,
ModelBinder = oldBindingContext.ModelBinder, ModelBinder = oldBindingContext.ModelBinder,
HttpContext = oldBindingContext.HttpContext HttpContext = oldBindingContext.HttpContext
}; };
// validation is expensive to create, so copy it over if we can // validation is expensive to create, so copy it over if we can

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public Task<bool> BindModelAsync(ModelBindingContext bindingContext) public Task<bool> BindModelAsync(ModelBindingContext bindingContext)
{ {
Type binderType = ResolveBinderType(bindingContext.ModelType); var binderType = ResolveBinderType(bindingContext.ModelType);
if (binderType != null) if (binderType != null)
{ {
var binder = (IModelBinder)_activator.CreateInstance(_serviceProvider, binderType); var binder = (IModelBinder)_activator.CreateInstance(_serviceProvider, binderType);
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
if (modelType.IsArray) if (modelType.IsArray)
{ {
Type elementType = modelType.GetElementType(); var elementType = modelType.GetElementType();
return typeof(ArrayModelBinder<>).MakeGenericType(elementType); return typeof(ArrayModelBinder<>).MakeGenericType(elementType);
} }
return null; return null;
@ -78,27 +78,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
openBinderType: typeof(KeyValuePairModelBinder<,>)); openBinderType: typeof(KeyValuePairModelBinder<,>));
} }
/// <remarks> /// <remarks>
/// Example: GetGenericBinder(typeof(IList<>), typeof(List<>), typeof(ListBinder<>), ...) means that the ListBinder<T> /// Example: GetGenericBinder(typeof(IList<>), typeof(List<>), typeof(ListBinder<>), ...) means that the
/// type can update models that implement IList<T>, and if for some reason the existing model instance is not /// ListBinder<T> type can update models that implement IList<T>, and if for some reason the existing model
/// updatable the binder will create a List<T> object and bind to that instead. This method will return ListBinder<T> /// instance is not updatable the binder will create a List<T> object and bind to that instead. This method
/// or null, depending on whether the type and updatability checks succeed. /// will return ListBinder<T> or null, depending on whether the type and updatability checks succeed.
/// </remarks> /// </remarks>
private static Type GetGenericBinderType(Type supportedInterfaceType, Type newInstanceType, Type openBinderType, Type modelType) private static Type GetGenericBinderType(Type supportedInterfaceType,
Type newInstanceType,
Type openBinderType,
Type modelType)
{ {
Contract.Assert(supportedInterfaceType != null); Contract.Assert(supportedInterfaceType != null);
Contract.Assert(openBinderType != null); Contract.Assert(openBinderType != null);
Contract.Assert(modelType != null); Contract.Assert(modelType != null);
Type[] modelTypeArguments = GetGenericBinderTypeArgs(supportedInterfaceType, modelType); var modelTypeArguments = GetGenericBinderTypeArgs(supportedInterfaceType, modelType);
if (modelTypeArguments == null) if (modelTypeArguments == null)
{ {
return null; return null;
} }
Type closedNewInstanceType = newInstanceType.MakeGenericType(modelTypeArguments); var closedNewInstanceType = newInstanceType.MakeGenericType(modelTypeArguments);
if (!modelType.GetTypeInfo().IsAssignableFrom(closedNewInstanceType.GetTypeInfo())) if (!modelType.GetTypeInfo().IsAssignableFrom(closedNewInstanceType.GetTypeInfo()))
{ {
return null; return null;
@ -110,14 +112,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Get the generic arguments for the binder, based on the model type. Or null if not compatible. // Get the generic arguments for the binder, based on the model type. Or null if not compatible.
private static Type[] GetGenericBinderTypeArgs(Type supportedInterfaceType, Type modelType) private static Type[] GetGenericBinderTypeArgs(Type supportedInterfaceType, Type modelType)
{ {
TypeInfo modelTypeInfo = modelType.GetTypeInfo(); var modelTypeInfo = modelType.GetTypeInfo();
if (!modelTypeInfo.IsGenericType || modelTypeInfo.IsGenericTypeDefinition) if (!modelTypeInfo.IsGenericType || modelTypeInfo.IsGenericTypeDefinition)
{ {
// not a closed generic type // not a closed generic type
return null; return null;
} }
Type[] modelTypeArguments = modelTypeInfo.GenericTypeArguments; var modelTypeArguments = modelTypeInfo.GenericTypeArguments;
if (modelTypeArguments.Length != supportedInterfaceType.GetTypeInfo().GenericTypeParameters.Length) if (modelTypeArguments.Length != supportedInterfaceType.GetTypeInfo().GenericTypeParameters.Length)
{ {
// wrong number of generic type arguments // wrong number of generic type arguments
@ -126,6 +128,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return modelTypeArguments; return modelTypeArguments;
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // 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. // 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.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Internal;
@ -12,10 +11,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public async Task<bool> BindModelAsync(ModelBindingContext bindingContext) public async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
{ {
ModelBindingHelper.ValidateBindingContext(bindingContext, typeof(KeyValuePair<TKey, TValue>), allowNullModel: true); ModelBindingHelper.ValidateBindingContext(bindingContext,
typeof(KeyValuePair<TKey, TValue>),
allowNullModel: true);
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "key"); var keyResult = await TryBindStrongModel<TKey>(bindingContext, "key");
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "value"); var valueResult = await TryBindStrongModel<TValue>(bindingContext, "value");
if (keyResult.Success && valueResult.Success) if (keyResult.Success && valueResult.Success)
{ {
@ -27,21 +28,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
internal async Task<BindResult<TModel>> TryBindStrongModel<TModel>(ModelBindingContext parentBindingContext, internal async Task<BindResult<TModel>> TryBindStrongModel<TModel>(ModelBindingContext parentBindingContext,
string propertyName) string propertyName)
{ {
ModelBindingContext propertyBindingContext = new ModelBindingContext(parentBindingContext) var propertyBindingContext = new ModelBindingContext(parentBindingContext)
{ {
ModelMetadata = parentBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null, modelType: typeof(TModel)), ModelMetadata = parentBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null,
modelType: typeof(TModel)),
ModelName = ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName) ModelName = ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName)
}; };
if (await propertyBindingContext.ModelBinder.BindModelAsync(propertyBindingContext)) if (await propertyBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
{ {
object untypedModel = propertyBindingContext.Model; var untypedModel = propertyBindingContext.Model;
var model = ModelBindingHelper.CastOrDefault<TModel>(untypedModel); var model = ModelBindingHelper.CastOrDefault<TModel>(untypedModel);
parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode); parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode);
return new BindResult<TModel>(true, model); return new BindResult<TModel>(success: true, model: model);
} }
return new BindResult<TModel>(false, default(TModel)); return new BindResult<TModel>(success: false, model: default(TModel));
} }
internal sealed class BindResult<TModel> internal sealed class BindResult<TModel>

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -88,13 +87,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return true; return true;
} }
private ComplexModelDto CreateAndPopulateDto(ModelBindingContext bindingContext, IEnumerable<ModelMetadata> propertyMetadatas) private ComplexModelDto CreateAndPopulateDto(ModelBindingContext bindingContext,
IEnumerable<ModelMetadata> propertyMetadatas)
{ {
// create a DTO and call into the DTO binder // create a DTO and call into the DTO binder
var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas); var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
var dtoBindingContext = new ModelBindingContext(bindingContext) var dtoBindingContext = new ModelBindingContext(bindingContext)
{ {
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(() => originalDto, typeof(ComplexModelDto)), ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
typeof(ComplexModelDto)),
ModelName = bindingContext.ModelName ModelName = bindingContext.ModelName
}; };
@ -104,13 +105,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
protected virtual object CreateModel(ModelBindingContext bindingContext) protected virtual object CreateModel(ModelBindingContext bindingContext)
{ {
// If the Activator throws an exception, we want to propagate it back up the call stack, since the application // If the Activator throws an exception, we want to propagate it back up the call stack, since the
// developer should know that this was an invalid type to try to bind to. // application developer should know that this was an invalid type to try to bind to.
return Activator.CreateInstance(bindingContext.ModelType); return Activator.CreateInstance(bindingContext.ModelType);
} }
// Called when the property setter null check failed, allows us to add our own error message to ModelState. // Called when the property setter null check failed, allows us to add our own error message to ModelState.
internal static EventHandler<ModelValidatedEventArgs> CreateNullCheckFailedHandler(ModelMetadata modelMetadata, object incomingValue) internal static EventHandler<ModelValidatedEventArgs> CreateNullCheckFailedHandler(ModelMetadata modelMetadata,
object incomingValue)
{ {
return (sender, e) => return (sender, e) =>
{ {
@ -120,8 +122,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (validationState == ModelValidationState.Unvalidated) if (validationState == ModelValidationState.Unvalidated)
{ {
// Tracked via https://github.com/aspnet/Mvc/issues/450
// TODO: Revive ModelBinderConfig // TODO: Revive ModelBinderConfig
// string errorMessage = ModelBinderConfig.ValueRequiredErrorMessageProvider(e.ValidationContext, modelMetadata, incomingValue); // var errorMessage = ModelBinderConfig.ValueRequiredErrorMessageProvider(e.ValidationContext,
// modelMetadata,
// incomingValue);
var errorMessage = "A value is required."; var errorMessage = "A value is required.";
if (errorMessage != null) if (errorMessage != null)
{ {
@ -145,7 +150,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return bindingContext.ModelMetadata.Properties return bindingContext.ModelMetadata.Properties
.Where(propertyMetadata => .Where(propertyMetadata =>
(validationInfo.RequiredProperties.Contains(propertyMetadata.PropertyName) || (validationInfo.RequiredProperties.Contains(propertyMetadata.PropertyName) ||
!validationInfo.SkipProperties.Contains(propertyMetadata.PropertyName)) && !validationInfo.SkipProperties.Contains(propertyMetadata.PropertyName)) &&
CanUpdateProperty(propertyMetadata)); CanUpdateProperty(propertyMetadata));
} }
@ -199,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto) internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
{ {
var validationInfo = GetPropertyValidationInfo(bindingContext); var validationInfo = GetPropertyValidationInfo(bindingContext);
// Eliminate provided properties from requiredProperties; leaving just *missing* required properties. // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
validationInfo.RequiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName)); validationInfo.RequiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName));
@ -240,21 +245,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (dtoResult != null) if (dtoResult != null)
{ {
IModelValidator requiredValidator; IModelValidator requiredValidator;
validationInfo.RequiredValidators.TryGetValue(propertyMetadata.PropertyName, out requiredValidator); validationInfo.RequiredValidators.TryGetValue(propertyMetadata.PropertyName,
out requiredValidator);
SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator); SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode); bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode);
} }
} }
} }
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We're recording this exception so that we can act on it later.")]
protected virtual void SetProperty(ModelBindingContext bindingContext, protected virtual void SetProperty(ModelBindingContext bindingContext,
ModelMetadata propertyMetadata, ModelMetadata propertyMetadata,
ComplexModelDtoResult dtoResult, ComplexModelDtoResult dtoResult,
IModelValidator requiredValidator) IModelValidator requiredValidator)
{ {
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase;
var property = bindingContext.ModelType var property = bindingContext.ModelType
.GetProperty(propertyMetadata.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase); .GetProperty(propertyMetadata.PropertyName, bindingFlags);
if (property == null || !property.CanWrite) if (property == null || !property.CanWrite)
{ {
@ -265,7 +271,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var value = dtoResult.Model ?? GetPropertyDefaultValue(property); var value = dtoResult.Model ?? GetPropertyDefaultValue(property);
propertyMetadata.Model = value; propertyMetadata.Model = value;
// 'Required' validators need to run first so that we can provide useful error messages if // 'Required' validators need to run first so that we can provide useful error messages if
// the property setters throw, e.g. if we're setting entity keys to null. // the property setters throw, e.g. if we're setting entity keys to null.
if (value == null) if (value == null)
@ -295,7 +300,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// don't display a duplicate error message if a binding error has already occurred for this field // don't display a duplicate error message if a binding error has already occurred for this field
var targetInvocationException = ex as TargetInvocationException; var targetInvocationException = ex as TargetInvocationException;
if (targetInvocationException != null && if (targetInvocationException != null &&
targetInvocationException.InnerException != null) targetInvocationException.InnerException != null)
{ {
ex = targetInvocationException.InnerException; ex = targetInvocationException.InnerException;

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Internal;
@ -11,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public async Task<bool> BindModelAsync(ModelBindingContext bindingContext) public async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
{ {
ValueProviderResult valueProviderResult = await GetCompatibleValueProviderResult(bindingContext); var valueProviderResult = await GetCompatibleValueProviderResult(bindingContext);
if (valueProviderResult == null) if (valueProviderResult == null)
{ {
// conversion would have failed // conversion would have failed
@ -19,25 +18,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
object model = valueProviderResult.RawValue; var model = valueProviderResult.RawValue;
ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model); ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model);
bindingContext.Model = model; bindingContext.Model = model;
// TODO: Determine if we need IBodyValidator here. // TODO: Determine if we need IBodyValidator here.
return true; return true;
} }
internal static async Task<ValueProviderResult> GetCompatibleValueProviderResult(ModelBindingContext bindingContext) internal static async Task<ValueProviderResult> GetCompatibleValueProviderResult(ModelBindingContext context)
{ {
ModelBindingHelper.ValidateBindingContext(bindingContext); ModelBindingHelper.ValidateBindingContext(context);
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); var valueProviderResult = await context.ValueProvider.GetValueAsync(context.ModelName);
if (valueProviderResult == null) if (valueProviderResult == null)
{ {
return null; // the value doesn't exist return null; // the value doesn't exist
} }
if (!bindingContext.ModelType.IsCompatibleWith(valueProviderResult.RawValue)) if (!context.ModelType.IsCompatibleWith(valueProviderResult.RawValue))
{ {
return null; // value is of incompatible type return null; // value is of incompatible type
} }

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
_supportedMediaTypes = new List<string> _supportedMediaTypes = new List<string>
{ {
"application/json", "application/json",
"text/json" "text/json"
}; };
@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelType = context.Metadata.ModelType; var modelType = context.Metadata.ModelType;
context.Model = modelType.GetTypeInfo().IsValueType ? Activator.CreateInstance(modelType) : context.Model = modelType.GetTypeInfo().IsValueType ? Activator.CreateInstance(modelType) :
null; null;
return ; return;
} }
// Get the character encoding for the content // Get the character encoding for the content
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new JsonTextReader(new StreamReader(readStream, effectiveEncoding)); return new JsonTextReader(new StreamReader(readStream, effectiveEncoding));
} }
// <summary> /// <summary>
/// Called during deserialization to get the <see cref="JsonSerializer"/>. /// Called during deserialization to get the <see cref="JsonSerializer"/>.
/// </summary> /// </summary>
/// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns> /// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns>
@ -150,8 +150,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var exception = e.ErrorContext.Error; var exception = e.ErrorContext.Error;
context.ModelState.AddModelError(e.ErrorContext.Path, e.ErrorContext.Error); context.ModelState.AddModelError(e.ErrorContext.Path, e.ErrorContext.Error);
// Error must always be marked as handled // Error must always be marked as handled
// Failure to do so can cause the exception to be rethrown at every recursive level and overflow the // Failure to do so can cause the exception to be rethrown at every recursive level and
// stack for x64 CLR processes // overflow the stack for x64 CLR processes
e.ErrorContext.Handled = true; e.ErrorContext.Handled = true;
}; };
jsonSerializer.Error += errorHandler; jsonSerializer.Error += errorHandler;

View File

@ -37,7 +37,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
// TODO: Http exception // TODO: Http exception
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "415: Unsupported content type {0}", contentType)); throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"415: Unsupported content type {0}",
contentType));
} }
} }
} }

View File

@ -10,14 +10,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
public static class CollectionExtensions public static class CollectionExtensions
{ {
/// <summary> /// <summary>
/// Convert an ICollection to an array, removing null values. Fast path for case where there are no null values. /// Convert an ICollection to an array, removing null values. Fast path for case where
/// there are no null values.
/// </summary> /// </summary>
public static T[] ToArrayWithoutNulls<T>(this ICollection<T> collection) where T : class public static T[] ToArrayWithoutNulls<T>(this ICollection<T> collection) where T : class
{ {
Contract.Assert(collection != null); Contract.Assert(collection != null);
T[] result = new T[collection.Count]; var result = new T[collection.Count];
int count = 0; var count = 0;
foreach (T value in collection) foreach (T value in collection)
{ {
if (value != null) if (value != null)
@ -32,7 +33,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
} }
else else
{ {
T[] trimmedResult = new T[count]; var trimmedResult = new T[count];
Array.Copy(result, trimmedResult, count); Array.Copy(result, trimmedResult, count);
return trimmedResult; return trimmedResult;
} }

View File

@ -8,12 +8,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
{ {
public static class CollectionModelBinderUtil public static class CollectionModelBinderUtil
{ {
public static IEnumerable<string> GetIndexNamesFromValueProviderResult(ValueProviderResult valueProviderResultIndex) public static IEnumerable<string> GetIndexNamesFromValueProviderResult(ValueProviderResult valueProviderResult)
{ {
IEnumerable<string> indexNames = null; IEnumerable<string> indexNames = null;
if (valueProviderResultIndex != null) if (valueProviderResult != null)
{ {
string[] indexes = (string[])valueProviderResultIndex.ConvertTo(typeof(string[])); var indexes = (string[])valueProviderResult.ConvertTo(typeof(string[]));
if (indexes != null && indexes.Length > 0) if (indexes != null && indexes.Length > 0)
{ {
indexNames = indexes; indexNames = indexes;
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
} }
collection.Clear(); collection.Clear();
foreach (TElement element in incomingElements) foreach (var element in incomingElements)
{ {
collection.Add(element); collection.Add(element);
} }

View File

@ -8,8 +8,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
{ {
public static class DictionaryHelper public static class DictionaryHelper
{ {
public static IEnumerable<KeyValuePair<string, TValue>> FindKeysWithPrefix<TValue>([NotNull] IDictionary<string, TValue> dictionary, public static IEnumerable<KeyValuePair<string, TValue>> FindKeysWithPrefix<TValue>(
[NotNull] string prefix) [NotNull] IDictionary<string, TValue> dictionary,
[NotNull] string prefix)
{ {
TValue exactMatchValue; TValue exactMatchValue;
if (dictionary.TryGetValue(prefix, out exactMatchValue)) if (dictionary.TryGetValue(prefix, out exactMatchValue))

View File

@ -18,12 +18,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
var tokens = headerValue.Split(new[] { ';' }, 2); var tokens = headerValue.Split(new[] { ';' }, 2);
string charSet = null; string charSet = null;
if (tokens.Length > 1 && tokens[1].TrimStart().StartsWith(CharSetToken, StringComparison.OrdinalIgnoreCase)) if (tokens.Length > 1 &&
tokens[1].TrimStart().StartsWith(CharSetToken, StringComparison.OrdinalIgnoreCase))
{ {
charSet = tokens[1].TrimStart().Substring(CharSetToken.Length); charSet = tokens[1].TrimStart().Substring(CharSetToken.Length);
} }
return new ContentTypeHeaderValue(tokens[0], charSet); return new ContentTypeHeaderValue(tokens[0], charSet);
} }
return null; return null;
} }

View File

@ -26,13 +26,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
internal static string CreatePropertyModelName(string prefix, string propertyName) internal static string CreatePropertyModelName(string prefix, string propertyName)
{ {
if (String.IsNullOrEmpty(prefix)) if (string.IsNullOrEmpty(prefix))
{ {
return propertyName ?? String.Empty; return propertyName ?? string.Empty;
} }
else if (String.IsNullOrEmpty(propertyName)) else if (string.IsNullOrEmpty(propertyName))
{ {
return prefix ?? String.Empty; return prefix ?? string.Empty;
} }
else else
{ {
@ -40,9 +40,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
} }
} }
internal static Type GetPossibleBinderInstanceType(Type closedModelType, Type openModelType, Type openBinderType) internal static Type GetPossibleBinderInstanceType(Type closedModelType,
Type openModelType,
Type openBinderType)
{ {
Type[] typeArguments = TypeExtensions.GetTypeArgumentsIfMatch(closedModelType, openModelType); var typeArguments = TypeExtensions.GetTypeArgumentsIfMatch(closedModelType, openModelType);
return (typeArguments != null) ? openBinderType.MakeGenericType(typeArguments) : null; return (typeArguments != null) ? openBinderType.MakeGenericType(typeArguments) : null;
} }
@ -50,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
{ {
if (model is string && if (model is string &&
modelMetadata.ConvertEmptyStringToNull && modelMetadata.ConvertEmptyStringToNull &&
String.IsNullOrWhiteSpace(model as string)) string.IsNullOrWhiteSpace(model as string))
{ {
model = null; model = null;
} }
@ -64,7 +66,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
} }
} }
internal static void ValidateBindingContext(ModelBindingContext bindingContext, Type requiredType, bool allowNullModel) internal static void ValidateBindingContext(ModelBindingContext bindingContext,
Type requiredType,
bool allowNullModel)
{ {
ValidateBindingContext(bindingContext); ValidateBindingContext(bindingContext);
@ -80,10 +84,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
throw new ArgumentException(message, "bindingContext"); throw new ArgumentException(message, "bindingContext");
} }
if (bindingContext.Model != null && !bindingContext.ModelType.GetTypeInfo().IsAssignableFrom(requiredType.GetTypeInfo())) if (bindingContext.Model != null &&
!bindingContext.ModelType.GetTypeInfo().IsAssignableFrom(requiredType.GetTypeInfo()))
{ {
var message = Resources.FormatModelBinderUtil_ModelInstanceIsWrong( var message = Resources.FormatModelBinderUtil_ModelInstanceIsWrong(
bindingContext.Model.GetType(), bindingContext.Model.GetType(),
requiredType); requiredType);
throw new ArgumentException(message, "bindingContext"); throw new ArgumentException(message, "bindingContext");
} }

View File

@ -30,8 +30,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
return _sortedValues.Length > 0; // only match empty string when we have some value return _sortedValues.Length > 0; // only match empty string when we have some value
} }
PrefixComparer prefixComparer = new PrefixComparer(prefix); var prefixComparer = new PrefixComparer(prefix);
bool containsPrefix = Array.BinarySearch(_sortedValues, prefix, prefixComparer) > -1; var containsPrefix = Array.BinarySearch(_sortedValues, prefix, prefixComparer) > -1;
if (!containsPrefix) if (!containsPrefix)
{ {
// If there's something in the search boundary that starts with the same name // If there's something in the search boundary that starts with the same name
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
// - "abc"/"foo[abc]" // - "abc"/"foo[abc]"
internal IDictionary<string, string> GetKeysFromPrefix(string prefix) internal IDictionary<string, string> GetKeysFromPrefix(string prefix)
{ {
IDictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in _originalValues) foreach (var entry in _originalValues)
{ {
@ -79,10 +79,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
private static void GetKeyFromEmptyPrefix(string entry, IDictionary<string, string> results) private static void GetKeyFromEmptyPrefix(string entry, IDictionary<string, string> results)
{ {
string key; var dotPosition = entry.IndexOf('.');
int dotPosition = entry.IndexOf('.'); var bracketPosition = entry.IndexOf('[');
int bracketPosition = entry.IndexOf('['); var delimiterPosition = -1;
int delimiterPosition = -1;
if (dotPosition == -1) if (dotPosition == -1)
{ {
@ -103,20 +102,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
} }
} }
key = delimiterPosition == -1 ? entry : entry.Substring(0, delimiterPosition); var key = delimiterPosition == -1 ? entry : entry.Substring(0, delimiterPosition);
results[key] = key; results[key] = key;
} }
private static void GetKeyFromNonEmptyPrefix(string prefix, string entry, IDictionary<string, string> results) private static void GetKeyFromNonEmptyPrefix(string prefix, string entry, IDictionary<string, string> results)
{ {
string key = null; string key;
string fullName = null; string fullName;
int keyPosition = prefix.Length + 1; var keyPosition = prefix.Length + 1;
switch (entry[prefix.Length]) switch (entry[prefix.Length])
{ {
case '.': case '.':
int dotPosition = entry.IndexOf('.', keyPosition); var dotPosition = entry.IndexOf('.', keyPosition);
if (dotPosition == -1) if (dotPosition == -1)
{ {
dotPosition = entry.Length; dotPosition = entry.Length;
@ -127,7 +126,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
break; break;
case '[': case '[':
int bracketPosition = entry.IndexOf(']', keyPosition); var bracketPosition = entry.IndexOf(']', keyPosition);
if (bracketPosition == -1) if (bracketPosition == -1)
{ {
// Malformed for dictionary // Malformed for dictionary
@ -189,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
private sealed class PrefixComparer : IComparer<String> private sealed class PrefixComparer : IComparer<String>
{ {
private string _prefix; private readonly string _prefix;
public PrefixComparer(string prefix) public PrefixComparer(string prefix)
{ {
@ -198,7 +197,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
public int Compare(string x, string y) public int Compare(string x, string y)
{ {
string testString = Object.ReferenceEquals(x, _prefix) ? y : x; var testString = object.ReferenceEquals(x, _prefix) ? y : x;
if (IsPrefixMatch(_prefix, testString)) if (IsPrefixMatch(_prefix, testString))
{ {
return 0; return 0;

View File

@ -14,14 +14,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public abstract class AssociatedMetadataProvider<TModelMetadata> : IModelMetadataProvider public abstract class AssociatedMetadataProvider<TModelMetadata> : IModelMetadataProvider
where TModelMetadata : ModelMetadata where TModelMetadata : ModelMetadata
{ {
private readonly ConcurrentDictionary<Type, TypeInformation> _typeInfoCache = new ConcurrentDictionary<Type, TypeInformation>(); private readonly ConcurrentDictionary<Type, TypeInformation> _typeInfoCache =
new ConcurrentDictionary<Type, TypeInformation>();
public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType) public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType)
{ {
return GetMetadataForPropertiesCore(container, containerType); return GetMetadataForPropertiesCore(container, containerType);
} }
public ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, [NotNull] Type containerType, [NotNull] string propertyName) public ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
[NotNull] Type containerType,
[NotNull] string propertyName)
{ {
if (string.IsNullOrEmpty(propertyName)) if (string.IsNullOrEmpty(propertyName))
{ {
@ -32,7 +35,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
PropertyInformation propertyInfo; PropertyInformation propertyInfo;
if (!typeInfo.Properties.TryGetValue(propertyName, out propertyInfo)) if (!typeInfo.Properties.TryGetValue(propertyName, out propertyInfo))
{ {
throw new ArgumentException(Resources.FormatCommon_PropertyNotFound(containerType, propertyName), "propertyName"); var message = Resources.FormatCommon_PropertyNotFound(containerType, propertyName);
throw new ArgumentException(message, "propertyName");
} }
return CreatePropertyMetadata(modelAccessor, propertyInfo); return CreatePropertyMetadata(modelAccessor, propertyInfo);
@ -40,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType) public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType)
{ {
TModelMetadata prototype = GetTypeInformation(modelType).Prototype; var prototype = GetTypeInformation(modelType).Prototype;
return CreateMetadataFromPrototype(prototype, modelAccessor); return CreateMetadataFromPrototype(prototype, modelAccessor);
} }
@ -51,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
string propertyName); string propertyName);
// Override for applying the prototype + modelAccess to yield the final metadata // Override for applying the prototype + modelAccess to yield the final metadata
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype, protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype,
Func<object> modelAccessor); Func<object> modelAccessor);
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType) private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
@ -82,7 +86,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private TypeInformation GetTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes = null) private TypeInformation GetTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes = null)
{ {
// This retrieval is implemented as a TryGetValue/TryAdd instead of a GetOrAdd to avoid the performance cost of creating instance delegates // This retrieval is implemented as a TryGetValue/TryAdd instead of a GetOrAdd
// to avoid the performance cost of creating instance delegates
TypeInformation typeInfo; TypeInformation typeInfo;
if (!_typeInfoCache.TryGetValue(type, out typeInfo)) if (!_typeInfoCache.TryGetValue(type, out typeInfo))
{ {
@ -102,7 +107,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
var info = new TypeInformation var info = new TypeInformation
{ {
Prototype = CreateMetadataPrototype(attributes, containerType: null, modelType: type, propertyName: null) Prototype = CreateMetadataPrototype(attributes,
containerType: null,
modelType: type,
propertyName: null)
}; };
var properties = new Dictionary<string, PropertyInformation>(); var properties = new Dictionary<string, PropertyInformation>();
@ -155,15 +163,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Uses Lightweight Code Gen to generate a tiny delegate that gets the property value // Uses Lightweight Code Gen to generate a tiny delegate that gets the property value
// This is an optimization to avoid having to go through the much slower System.Reflection APIs // This is an optimization to avoid having to go through the much slower System.Reflection APIs
// e.g. generates (object o) => (Person)o.Id // e.g. generates (object o) => (Person)o.Id
private static Func<object, object> CreateDynamicValueAccessor(MethodInfo getMethodInfo, Type declaringType, string propertyName) private static Func<object, object> CreateDynamicValueAccessor(MethodInfo getMethodInfo,
Type declaringType,
string propertyName)
{ {
Contract.Assert(getMethodInfo != null && getMethodInfo.IsPublic && !getMethodInfo.IsStatic); Contract.Assert(getMethodInfo != null && getMethodInfo.IsPublic && !getMethodInfo.IsStatic);
var declaringTypeInfo = declaringType.GetTypeInfo(); var declaringTypeInfo = declaringType.GetTypeInfo();
var propertyType = getMethodInfo.ReturnType; var propertyType = getMethodInfo.ReturnType;
var dynamicMethod = new DynamicMethod("Get" + propertyName + "From" + declaringType.Name, var dynamicMethod = new DynamicMethod("Get" + propertyName + "From" + declaringType.Name,
typeof(object), typeof(object),
new [] { typeof(object) }); new[] { typeof(object) });
var ilg = dynamicMethod.GetILGenerator(); var ilg = dynamicMethod.GetILGenerator();
// Load the container onto the stack, convert from object => declaring type for the property // Load the container onto the stack, convert from object => declaring type for the property

View File

@ -2,10 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
@ -33,7 +29,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Constructor for creating real instances of the metadata class based on a prototype // Constructor for creating real instances of the metadata class based on a prototype
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor) protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
: base(prototype.Provider, prototype.ContainerType, modelAccessor, prototype.ModelType, prototype.PropertyName) : base(prototype.Provider,
prototype.ContainerType,
modelAccessor,
prototype.ModelType,
prototype.PropertyName)
{ {
CacheKey = prototype.CacheKey; CacheKey = prototype.CacheKey;
PrototypeCache = prototype.PrototypeCache; PrototypeCache = prototype.PrototypeCache;
@ -43,8 +43,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
// Constructor for creating the prototype instances of the metadata class // Constructor for creating the prototype instances of the metadata class
protected CachedModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Type modelType, string propertyName, TPrototypeCache prototypeCache) protected CachedModelMetadata(DataAnnotationsModelMetadataProvider provider,
: base(provider, containerType, null /* modelAccessor */, modelType, propertyName) Type containerType,
Type modelType,
string propertyName,
TPrototypeCache prototypeCache)
: base(provider, containerType, modelAccessor: null, modelType: modelType, propertyName: propertyName)
{ {
PrototypeCache = prototypeCache; PrototypeCache = prototypeCache;
} }

View File

@ -8,17 +8,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class EmptyModelMetadataProvider : AssociatedMetadataProvider<ModelMetadata> public class EmptyModelMetadataProvider : AssociatedMetadataProvider<ModelMetadata>
{ {
protected override ModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, protected override ModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes,
Type containerType, Type containerType,
Type modelType, Type modelType,
string propertyName) string propertyName)
{ {
return new ModelMetadata(this, containerType, null, modelType, propertyName); return new ModelMetadata(this, containerType, null, modelType, propertyName);
} }
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype, Func<object> modelAccessor) protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype,
Func<object> modelAccessor)
{ {
return new ModelMetadata(this, prototype.ContainerType, modelAccessor, prototype.ModelType, prototype.PropertyName); return new ModelMetadata(this,
prototype.ContainerType,
modelAccessor,
prototype.ModelType,
prototype.PropertyName);
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Internal;
@ -27,10 +26,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private Type _realModelType; private Type _realModelType;
private string _simpleDisplayText; private string _simpleDisplayText;
public ModelMetadata([NotNull] IModelMetadataProvider provider, public ModelMetadata([NotNull] IModelMetadataProvider provider,
Type containerType, Type containerType,
Func<object> modelAccessor, Func<object> modelAccessor,
[NotNull] Type modelType, [NotNull] Type modelType,
string propertyName) string propertyName)
{ {
Provider = provider; Provider = provider;
@ -181,7 +180,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
} }
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The method is a delegating helper to choose among multiple property values")]
public virtual string GetDisplayName() public virtual string GetDisplayName()
{ {
return PropertyName ?? ModelType.Name; return PropertyName ?? ModelType.Name;

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
@ -57,14 +56,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
if (_modelName == null) if (_modelName == null)
{ {
_modelName = String.Empty; _modelName = string.Empty;
} }
return _modelName; return _modelName;
} }
set { _modelName = value; } set { _modelName = value; }
} }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is writeable to support unit testing")]
public ModelStateDictionary ModelState public ModelStateDictionary ModelState
{ {
get get
@ -121,7 +119,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
if (_propertyMetadata == null) if (_propertyMetadata == null)
{ {
_propertyMetadata = ModelMetadata.Properties.ToDictionary(m => m.PropertyName, StringComparer.OrdinalIgnoreCase); _propertyMetadata = ModelMetadata.Properties
.ToDictionary(m => m.PropertyName,
StringComparer.OrdinalIgnoreCase);
} }
return _propertyMetadata; return _propertyMetadata;

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public ModelError(string errorMessage) public ModelError(string errorMessage)
{ {
ErrorMessage = errorMessage ?? String.Empty; ErrorMessage = errorMessage ?? string.Empty;
} }
public Exception Exception { get; private set; } public Exception Exception { get; private set; }

View File

@ -11,7 +11,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class ModelStateDictionary : IDictionary<string, ModelState> public class ModelStateDictionary : IDictionary<string, ModelState>
{ {
private readonly IDictionary<string, ModelState> _innerDictionary = new Dictionary<string, ModelState>(StringComparer.OrdinalIgnoreCase); private readonly IDictionary<string, ModelState> _innerDictionary =
new Dictionary<string, ModelState>(StringComparer.OrdinalIgnoreCase);
public ModelStateDictionary() public ModelStateDictionary()
{ {
@ -54,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public ModelValidationState ValidationState public ModelValidationState ValidationState
{ {
get { return GetValidity(_innerDictionary); } get { return GetValidity(_innerDictionary); }
} }
public ModelState this[[NotNull] string key] public ModelState this[[NotNull] string key]
@ -67,7 +68,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
set set
{ {
if(value == null) if (value == null)
{ {
throw new ArgumentNullException("value"); throw new ArgumentNullException("value");
} }

View File

@ -18,9 +18,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[NotNull] ClientModelValidationContext context) [NotNull] ClientModelValidationContext context)
{ {
var errorMessage = ((CompareAttributeWrapper)Attribute).FormatErrorMessage(context); var errorMessage = ((CompareAttributeWrapper)Attribute).FormatErrorMessage(context);
var clientRule = new ModelClientValidationEqualToRule(errorMessage, var clientRule = new ModelClientValidationEqualToRule(errorMessage,
FormatPropertyForClientValidation(Attribute.OtherProperty)); FormatPropertyForClientValidation(Attribute.OtherProperty));
return new [] { clientRule }; return new[] { clientRule };
} }
private static string FormatPropertyForClientValidation(string property) private static string FormatPropertyForClientValidation(string property)
@ -50,9 +50,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public string FormatErrorMessage(ClientModelValidationContext context) public string FormatErrorMessage(ClientModelValidationContext context)
{ {
var displayName = context.ModelMetadata.GetDisplayName(); var displayName = context.ModelMetadata.GetDisplayName();
return string.Format(CultureInfo.CurrentCulture, return string.Format(CultureInfo.CurrentCulture,
ErrorMessageString, ErrorMessageString,
displayName, displayName,
GetOtherPropertyDisplayName(context)); GetOtherPropertyDisplayName(context));
} }

View File

@ -20,7 +20,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
_errorMessage = errorMessage; _errorMessage = errorMessage;
} }
public bool IsRequired { get { return false; } } public bool IsRequired
{
get { return false; }
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{ {

View File

@ -11,42 +11,51 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class InvalidModelValidatorProvider : AssociatedValidatorProvider public class InvalidModelValidatorProvider : AssociatedValidatorProvider
{ {
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
IEnumerable<Attribute> attributes) IEnumerable<Attribute> attributes)
{ {
if (metadata.ContainerType == null || String.IsNullOrEmpty(metadata.PropertyName)) if (metadata.ContainerType == null || string.IsNullOrEmpty(metadata.PropertyName))
{ {
// Validate that the type's fields and nonpublic properties don't have any validation attributes on them // Validate that the type's fields and nonpublic properties don't have any validation attributes on
// Validation only runs against public properties // them. Validation only runs against public properties
var type = metadata.ModelType; var type = metadata.ModelType;
var nonPublicProperties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance); var nonPublicProperties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var nonPublicProperty in nonPublicProperties) foreach (var nonPublicProperty in nonPublicProperties)
{ {
if (nonPublicProperty.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any()) if (nonPublicProperty.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any())
{ {
yield return new ErrorModelValidator(Resources.FormatValidationAttributeOnNonPublicProperty(nonPublicProperty.Name, type)); var message = Resources.FormatValidationAttributeOnNonPublicProperty(nonPublicProperty.Name,
type);
yield return new ErrorModelValidator(message);
} }
} }
var allFields = metadata.ModelType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var allFields = metadata.ModelType.GetFields(bindingFlags);
foreach (var field in allFields) foreach (var field in allFields)
{ {
if (field.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any()) if (field.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any())
{ {
yield return new ErrorModelValidator(Resources.FormatValidationAttributeOnField(field.Name, type)); var message = Resources.FormatValidationAttributeOnField(field.Name, type);
yield return new ErrorModelValidator(message);
} }
} }
} }
else else
{ {
// Validate that value-typed properties marked as [Required] are also marked as [DataMember(IsRequired=true)] // Validate that value-typed properties marked as [Required] are also marked as
// Certain formatters may not recognize a member as required if it's marked as [Required] but not [DataMember(IsRequired=true)] // [DataMember(IsRequired=true)]. Certain formatters may not recognize a member as required if it's
// This is not a problem for reference types because [Required] will still cause a model error to be raised after a null value is deserialized // marked as [Required] but not [DataMember(IsRequired=true)]. This is not a problem for reference
if (metadata.ModelType.GetTypeInfo().IsValueType && attributes.Any(attribute => attribute is RequiredAttribute)) // types because [Required] will still cause a model error to be raised after a null value is
// deserialized.
if (metadata.ModelType.GetTypeInfo().IsValueType &&
attributes.Any(attribute => attribute is RequiredAttribute))
{ {
if (!DataMemberModelValidatorProvider.IsRequiredDataMember(metadata.ContainerType, attributes)) if (!DataMemberModelValidatorProvider.IsRequiredDataMember(metadata.ContainerType, attributes))
{ {
yield return new ErrorModelValidator(Resources.FormatMissingDataMemberIsRequired(metadata.PropertyName, metadata.ContainerType)); var message = Resources.FormatMissingDataMemberIsRequired(metadata.PropertyName,
metadata.ContainerType);
yield return new ErrorModelValidator(message);
} }
} }
} }

View File

@ -17,8 +17,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
} }
public ModelValidationNode([NotNull] ModelMetadata modelMetadata, public ModelValidationNode([NotNull] ModelMetadata modelMetadata,
[NotNull] string modelStateKey, [NotNull] string modelStateKey,
IEnumerable<ModelValidationNode> childNodes) IEnumerable<ModelValidationNode> childNodes)
{ {
ModelMetadata = modelMetadata; ModelMetadata = modelMetadata;
@ -151,13 +151,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelState = validationContext.ModelState; var modelState = validationContext.ModelState;
var model = ModelMetadata.Model; var model = ModelMetadata.Model;
var updatedMetadata = validationContext.MetadataProvider.GetMetadataForType(() => model, ModelMetadata.ModelType); var updatedMetadata = validationContext.MetadataProvider.GetMetadataForType(() => model,
ModelMetadata.ModelType);
foreach (var propertyMetadata in updatedMetadata.Properties) foreach (var propertyMetadata in updatedMetadata.Properties)
{ {
// Only want to add errors to ModelState if something doesn't already exist for the property node, // Only want to add errors to ModelState if something doesn't already exist for the property node,
// else we could end up with duplicate or irrelevant error messages. // else we could end up with duplicate or irrelevant error messages.
var propertyKeyRoot = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName); var propertyKeyRoot = ModelBindingHelper.CreatePropertyModelName(ModelStateKey,
propertyMetadata.PropertyName);
if (modelState.GetFieldValidationState(propertyKeyRoot) == ModelValidationState.Unvalidated) if (modelState.GetFieldValidationState(propertyKeyRoot) == ModelValidationState.Unvalidated)
{ {
@ -167,7 +169,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
foreach (var propertyResult in propertyValidator.Validate(propertyValidationContext)) foreach (var propertyResult in propertyValidator.Validate(propertyValidationContext))
{ {
var thisErrorKey = ModelBindingHelper.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName); var thisErrorKey = ModelBindingHelper.CreatePropertyModelName(propertyKeyRoot,
propertyResult.MemberName);
modelState.AddModelError(thisErrorKey, propertyResult.Message); modelState.AddModelError(thisErrorKey, propertyResult.Message);
} }
} }
@ -187,7 +190,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// If the Model at the current node is null and there is no parent, we cannot validate, and the // If the Model at the current node is null and there is no parent, we cannot validate, and the
// DataAnnotationsModelValidator will throw. So we intercept here to provide a catch-all value-required // DataAnnotationsModelValidator will throw. So we intercept here to provide a catch-all value-required
// validation error // validation error
var modelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, ModelMetadata.GetDisplayName()); var modelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey,
ModelMetadata.GetDisplayName());
if (parentNode == null && ModelMetadata.Model == null) if (parentNode == null && ModelMetadata.Model == null)
{ {
modelState.AddModelError(modelStateKey, Resources.Validation_ValueNotFound); modelState.AddModelError(modelStateKey, Resources.Validation_ValueNotFound);
@ -201,13 +205,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var validator = validators[i]; var validator = validators[i];
foreach (var validationResult in validator.Validate(validationContext)) foreach (var validationResult in validator.Validate(validationContext))
{ {
var currentModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, validationResult.MemberName); var currentModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey,
validationResult.MemberName);
modelState.AddModelError(currentModelStateKey, validationResult.Message); modelState.AddModelError(currentModelStateKey, validationResult.Message);
} }
} }
} }
private static IEnumerable<IModelValidator> GetValidators(ModelValidationContext validationContext, ModelMetadata metadata) private static IEnumerable<IModelValidator> GetValidators(ModelValidationContext validationContext,
ModelMetadata metadata)
{ {
return validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata)); return validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata));
} }

View File

@ -17,9 +17,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[NotNull] ClientModelValidationContext context) [NotNull] ClientModelValidationContext context)
{ {
var errorMessage = GetErrorMessage(context.ModelMetadata); var errorMessage = GetErrorMessage(context.ModelMetadata);
return new[] { new ModelClientValidationStringLengthRule(errorMessage, var rule = new ModelClientValidationStringLengthRule(errorMessage,
Attribute.MinimumLength, Attribute.MinimumLength,
Attribute.MaximumLength) }; Attribute.MaximumLength);
return new[] { rule };
} }
} }
} }

View File

@ -4,16 +4,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "It is more fundamentally a value provider than a collection")]
public class CompositeValueProvider : Collection<IValueProvider>, IEnumerableValueProvider public class CompositeValueProvider : Collection<IValueProvider>, IEnumerableValueProvider
{ {
public CompositeValueProvider() public CompositeValueProvider()
: base() : base()
{ {
} }
@ -65,7 +63,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
private static Task<IDictionary<string, string>> GetKeysFromPrefixFromProvider(IValueProvider provider, private static Task<IDictionary<string, string>> GetKeysFromPrefixFromProvider(IValueProvider provider,
string prefix) string prefix)
{ {
var enumeratedProvider = provider as IEnumerableValueProvider; var enumeratedProvider = provider as IEnumerableValueProvider;

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
@ -15,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public IValueProvider GetValueProvider(RequestContext requestContext) public IValueProvider GetValueProvider(RequestContext requestContext)
{ {
var request = requestContext.HttpContext.Request; var request = requestContext.HttpContext.Request;
if (IsSupportedContentType(request)) if (IsSupportedContentType(request))
{ {
var culture = GetCultureInfo(request); var culture = GetCultureInfo(request);
@ -34,8 +33,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static CultureInfo GetCultureInfo(HttpRequest request) private static CultureInfo GetCultureInfo(HttpRequest request)
{ {
// TODO: Tracked via https://github.com/aspnet/HttpAbstractions/issues/10. Determine what's the right way to // TODO: Tracked via https://github.com/aspnet/HttpAbstractions/issues/10.
// map Accept-Language to culture. // Determine what's the right way to map Accept-Language to culture.
return CultureInfo.CurrentCulture; return CultureInfo.CurrentCulture;
} }
} }

View File

@ -255,7 +255,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var type = value.GetType(); var type = value.GetType();
if (type != typeof(string)) if (type != typeof(string))
{ {
string message = Resources.FormatValueProviderResult_NoConverterExists(type, destinationType); var message = Resources.FormatValueProviderResult_NoConverterExists(type, destinationType);
throw new InvalidOperationException(message); throw new InvalidOperationException(message);
} }
} }

View File

@ -8,50 +8,35 @@ using System.Linq;
using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class AssociatedMetadataProviderTest public class AssociatedMetadataProviderTest
{ {
// GetMetadataForProperties // GetMetadataForProperties
//[Fact]
//public void GetMetadataForPropertiesNullContainerTypeThrows()
//{
// // Arrange
// TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
// // Act & Assert
// ExceptionAssert.ThrowsArgumentNull(
// () => provider.GetMetadataForProperties(new Object(), containerType: null),
// "containerType");
//}
[Fact] [Fact]
public void GetMetadataForPropertiesCreatesMetadataForAllPropertiesOnModelWithPropertyValues() public void GetMetadataForPropertiesCreatesMetadataForAllPropertiesOnModelWithPropertyValues()
{ {
// Arrange // Arrange
PropertyModel model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 }; var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
// Act // Act
provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate // Call ToList() to force the lazy evaluation to evaluate
provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
// Assert // Assert
CreateMetadataPrototypeParams local = var local = provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) &&
provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "LocalAttributes");
m.PropertyName == "LocalAttributes");
Assert.Equal(typeof(int), local.ModelType); Assert.Equal(typeof(int), local.ModelType);
Assert.True(local.Attributes.Any(a => a is RequiredAttribute)); Assert.True(local.Attributes.Any(a => a is RequiredAttribute));
CreateMetadataPrototypeParams metadata = var metadata = provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) &&
provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MetadataAttributes");
m.PropertyName == "MetadataAttributes");
Assert.Equal(typeof(string), metadata.ModelType); Assert.Equal(typeof(string), metadata.ModelType);
Assert.True(metadata.Attributes.Any(a => a is RangeAttribute)); Assert.True(metadata.Attributes.Any(a => a is RangeAttribute));
CreateMetadataPrototypeParams mixed = var mixed = provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) &&
provider.CreateMetadataPrototypeLog.Single(m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MixedAttributes");
m.PropertyName == "MixedAttributes");
Assert.Equal(typeof(double), mixed.ModelType); Assert.Equal(typeof(double), mixed.ModelType);
Assert.True(mixed.Attributes.Any(a => a is RequiredAttribute)); Assert.True(mixed.Attributes.Any(a => a is RequiredAttribute));
Assert.True(mixed.Attributes.Any(a => a is RangeAttribute)); Assert.True(mixed.Attributes.Any(a => a is RangeAttribute));
@ -61,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
public void GetMetadataForPropertyWithNullContainerReturnsMetadataWithNullValuesForProperties() public void GetMetadataForPropertyWithNullContainerReturnsMetadataWithNullValuesForProperties()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
// Act // Act
provider.GetMetadataForProperties(null, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate provider.GetMetadataForProperties(null, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate
@ -80,7 +65,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows() public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgument( ExceptionAssert.ThrowsArgument(
@ -97,7 +82,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
public void GetMetadataForPropertyInvalidPropertyNameThrows() public void GetMetadataForPropertyInvalidPropertyNameThrows()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgument( ExceptionAssert.ThrowsArgument(
@ -110,67 +95,57 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
public void GetMetadataForPropertyWithLocalAttributes() public void GetMetadataForPropertyWithLocalAttributes()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
ModelMetadata metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(int), "LocalAttributes"); var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(int), "LocalAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata; provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act // Act
ModelMetadata result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes"); var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes");
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(metadata, result);
Assert.True(provider.CreateMetadataPrototypeLog.Single(parameters => parameters.PropertyName == "LocalAttributes").Attributes.Any(a => a is RequiredAttribute)); Assert.True(provider.CreateMetadataPrototypeLog
.Single(parameters => parameters.PropertyName == "LocalAttributes")
.Attributes.Any(a => a is RequiredAttribute));
} }
[Fact] [Fact]
public void GetMetadataForPropertyWithMetadataAttributes() public void GetMetadataForPropertyWithMetadataAttributes()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
ModelMetadata metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(string), "MetadataAttributes"); var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(string), "MetadataAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata; provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act // Act
ModelMetadata result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes"); var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes");
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(metadata, result);
CreateMetadataPrototypeParams parms = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MetadataAttributes"); var parmaters = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MetadataAttributes");
Assert.True(parms.Attributes.Any(a => a is RangeAttribute)); Assert.True(parmaters.Attributes.Any(a => a is RangeAttribute));
} }
[Fact] [Fact]
public void GetMetadataForPropertyWithMixedAttributes() public void GetMetadataForPropertyWithMixedAttributes()
{ {
// Arrange // Arrange
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
ModelMetadata metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(double), "MixedAttributes"); var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(double), "MixedAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata; provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act // Act
ModelMetadata result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes"); var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes");
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(metadata, result);
CreateMetadataPrototypeParams parms = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MixedAttributes"); var parms = provider.CreateMetadataPrototypeLog.Single(p => p.PropertyName == "MixedAttributes");
Assert.True(parms.Attributes.Any(a => a is RequiredAttribute)); Assert.True(parms.Attributes.Any(a => a is RequiredAttribute));
Assert.True(parms.Attributes.Any(a => a is RangeAttribute)); Assert.True(parms.Attributes.Any(a => a is RangeAttribute));
} }
// GetMetadataForType // GetMetadataForType
//[Fact]
//public void GetMetadataForTypeNullModelTypeThrows()
//{
// // Arrange
// TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
// // Act & Assert
// ExceptionAssert.ThrowsArgumentNull(
// () => provider.GetMetadataForType(() => new Object(), modelType: null),
// "modelType");
//}
#if NET45 // No ReadOnlyAttribute in K #if NET45 // No ReadOnlyAttribute in K
[Fact] [Fact]
public void GetMetadataForTypeIncludesAttributesOnType() public void GetMetadataForTypeIncludesAttributesOnType()
@ -207,7 +182,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
} }
private sealed class RequiredAttribute : Attribute private sealed class RequiredAttribute : Attribute
{ } {
}
private sealed class RangeAttribute : Attribute private sealed class RangeAttribute : Attribute
{ {
@ -228,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
} }
#endif #endif
class TestableAssociatedMetadataProvider : AssociatedMetadataProvider<ModelMetadata> private class TestableAssociatedMetadataProvider : AssociatedMetadataProvider<ModelMetadata>
{ {
public List<CreateMetadataPrototypeParams> CreateMetadataPrototypeLog = new List<CreateMetadataPrototypeParams>(); public List<CreateMetadataPrototypeParams> CreateMetadataPrototypeLog = new List<CreateMetadataPrototypeParams>();
public List<CreateMetadataFromPrototypeParams> CreateMetadataFromPrototypeLog = new List<CreateMetadataFromPrototypeParams>(); public List<CreateMetadataFromPrototypeParams> CreateMetadataFromPrototypeLog = new List<CreateMetadataFromPrototypeParams>();
@ -260,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
} }
} }
class CreateMetadataPrototypeParams private class CreateMetadataPrototypeParams
{ {
public IEnumerable<Attribute> Attributes { get; set; } public IEnumerable<Attribute> Attributes { get; set; }
public Type ContainerType { get; set; } public Type ContainerType { get; set; }
@ -268,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
public string PropertyName { get; set; } public string PropertyName { get; set; }
} }
class CreateMetadataFromPrototypeParams private class CreateMetadataFromPrototypeParams
{ {
public ModelMetadata Prototype { get; set; } public ModelMetadata Prototype { get; set; }
public object Model { get; set; } public object Model { get; set; }

View File

@ -9,7 +9,7 @@ using Moq;
#endif #endif
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class ModelMetadataTest public class ModelMetadataTest
{ {
@ -20,10 +20,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void DefaultValues() public void DefaultValues()
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
// Act // Act
ModelMetadata metadata = new ModelMetadata(provider.Object, typeof(Exception), () => "model", typeof(string), "propertyName"); var metadata = new ModelMetadata(provider.Object, typeof(Exception), () => "model", typeof(string), "propertyName");
// Assert // Assert
Assert.Equal(typeof(Exception), metadata.ContainerType); Assert.Equal(typeof(Exception), metadata.ContainerType);
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// IsComplexType // IsComplexType
struct IsComplexTypeModel private struct IsComplexTypeModel
{ {
} }
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void IsComplexTypeTestsReturnsFalseForSimpleTypes(Type type) public void IsComplexTypeTestsReturnsFalseForSimpleTypes(Type type)
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
// Act // Act
var modelMetadata = new ModelMetadata(provider.Object, null, null, type, null); var modelMetadata = new ModelMetadata(provider.Object, null, null, type, null);
@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void IsComplexTypeTestsReturnsTrueForComplexTypes(Type type) public void IsComplexTypeTestsReturnsTrueForComplexTypes(Type type)
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
// Act // Act
var modelMetadata = new ModelMetadata(provider.Object, null, null, type, null); var modelMetadata = new ModelMetadata(provider.Object, null, null, type, null);
@ -83,7 +83,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void IsNullableValueTypeTests() public void IsNullableValueTypeTests()
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
// Act & Assert // Act & Assert
Assert.False(new ModelMetadata(provider.Object, null, null, typeof(string), null).IsNullableValueType); Assert.False(new ModelMetadata(provider.Object, null, null, typeof(string), null).IsNullableValueType);
@ -98,16 +98,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void PropertiesCallsProvider() public void PropertiesCallsProvider()
{ {
// Arrange // Arrange
Type modelType = typeof(string); var modelType = typeof(string);
List<ModelMetadata> propertyMetadata = new List<ModelMetadata>(); var propertyMetadata = new List<ModelMetadata>();
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
ModelMetadata metadata = new ModelMetadata(provider.Object, null, null, modelType, null); var metadata = new ModelMetadata(provider.Object, null, null, modelType, null);
provider.Setup(p => p.GetMetadataForProperties(null, modelType)) provider.Setup(p => p.GetMetadataForProperties(null, modelType))
.Returns(propertyMetadata) .Returns(propertyMetadata)
.Verifiable(); .Verifiable();
// Act // Act
IEnumerable<ModelMetadata> result = metadata.Properties; var result = metadata.Properties;
// Assert // Assert
Assert.Equal(propertyMetadata, result.ToList()); Assert.Equal(propertyMetadata, result.ToList());
@ -118,31 +118,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[Fact] [Fact]
public void PropertiesListGetsResetWhenModelGetsReset() public void PropertiesListGetsResetWhenModelGetsReset()
{ {
// Dev10 Bug #923263
// Arrange // Arrange
IModelMetadataProvider provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, () => new Class1(), typeof(Class1), null); var metadata = new ModelMetadata(provider, null, () => new Class1(), typeof(Class1), null);
// Act // Act
ModelMetadata[] originalProps = metadata.Properties.ToArray(); var originalProps = metadata.Properties.ToArray();
metadata.Model = new Class2(); metadata.Model = new Class2();
ModelMetadata[] newProps = metadata.Properties.ToArray(); var newProps = metadata.Properties.ToArray();
// Assert // Assert
ModelMetadata originalProp = Assert.Single(originalProps); var originalProp = Assert.Single(originalProps);
Assert.Equal(typeof(string), originalProp.ModelType); Assert.Equal(typeof(string), originalProp.ModelType);
Assert.Equal("Prop1", originalProp.PropertyName); Assert.Equal("Prop1", originalProp.PropertyName);
ModelMetadata newProp = Assert.Single(newProps); var newProp = Assert.Single(newProps);
Assert.Equal(typeof(int), newProp.ModelType); Assert.Equal(typeof(int), newProp.ModelType);
Assert.Equal("Prop2", newProp.PropertyName); Assert.Equal("Prop2", newProp.PropertyName);
} }
class Class1 private class Class1
{ {
public string Prop1 { get; set; } public string Prop1 { get; set; }
} }
class Class2 private class Class2
{ {
public int Prop2 { get; set; } public int Prop2 { get; set; }
} }
@ -154,11 +153,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void ReturnsPropertyNameWhenSetAndDisplayNameIsNull() public void ReturnsPropertyNameWhenSetAndDisplayNameIsNull()
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
ModelMetadata metadata = new ModelMetadata(provider.Object, null, null, typeof(object), "PropertyName"); var metadata = new ModelMetadata(provider.Object, null, null, typeof(object), "PropertyName");
// Act // Act
string result = metadata.GetDisplayName(); var result = metadata.GetDisplayName();
// Assert // Assert
Assert.Equal("PropertyName", result); Assert.Equal("PropertyName", result);
@ -168,11 +167,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public void ReturnsTypeNameWhenPropertyNameAndDisplayNameAreNull() public void ReturnsTypeNameWhenPropertyNameAndDisplayNameAreNull()
{ {
// Arrange // Arrange
Mock<IModelMetadataProvider> provider = new Mock<IModelMetadataProvider>(); var provider = new Mock<IModelMetadataProvider>();
ModelMetadata metadata = new ModelMetadata(provider.Object, null, null, typeof(object), null); var metadata = new ModelMetadata(provider.Object, null, null, typeof(object), null);
// Act // Act
string result = metadata.GetDisplayName(); var result = metadata.GetDisplayName();
// Assert // Assert
Assert.Equal("Object", result); Assert.Equal("Object", result);
@ -186,7 +185,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public int IntField = 0; public int IntField = 0;
public string FirstName { get; set; } public string FirstName { get; set; }
public string LastName { get; set; } public string LastName { get; set; }
public Nullable<int> NullableIntValue { get; set; } public int? NullableIntValue { get; set; }
public int[] Array { get; set; } public int[] Array { get; set; }
public string this[int index] public string this[int index]

View File

@ -20,23 +20,41 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
get get
{ {
yield return new object[] { new RegularExpressionAttribute("abc"), yield return new object[]
typeof(RegularExpressionAttributeAdapter) }; {
new RegularExpressionAttribute("abc"),
typeof(RegularExpressionAttributeAdapter)
};
yield return new object[] { new MaxLengthAttribute(), yield return new object[]
typeof(MaxLengthAttributeAdapter) }; {
new MaxLengthAttribute(),
typeof(MaxLengthAttributeAdapter)
};
yield return new object[] { new MinLengthAttribute(1), yield return new object[]
typeof(MinLengthAttributeAdapter) }; {
new MinLengthAttribute(1),
typeof(MinLengthAttributeAdapter)
};
yield return new object[] { new RangeAttribute(1, 100), yield return new object[]
typeof(RangeAttributeAdapter) }; {
new RangeAttribute(1, 100),
typeof(RangeAttributeAdapter)
};
yield return new object[] { new StringLengthAttribute(6), yield return new object[]
typeof(StringLengthAttributeAdapter) }; {
new StringLengthAttribute(6),
typeof(StringLengthAttributeAdapter)
};
yield return new object[] { new RequiredAttribute(), yield return new object[]
typeof(RequiredAttributeAdapter) }; {
new RequiredAttribute(),
typeof(RequiredAttributeAdapter)
};
} }
} }

View File

@ -4,7 +4,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Testing;
#if NET45 #if NET45
using Moq; using Moq;
using Moq.Protected; using Moq.Protected;
@ -15,7 +14,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public class DataAnnotationsModelValidatorTest public class DataAnnotationsModelValidatorTest
{ {
private static DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider(); private static DataAnnotationsModelMetadataProvider _metadataProvider =
new DataAnnotationsModelMetadataProvider();
[Fact] [Fact]
public void ValuesSet() public void ValuesSet()
@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
get get
{ {
yield return new object[] yield return new object[]
{ {
_metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length"), _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length"),
"Length" "Length"
@ -52,7 +52,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
#if NET45 #if NET45
[Theory] [Theory]
[MemberData("ValidateSetsMemberNamePropertyDataSet")] [MemberData("ValidateSetsMemberNamePropertyDataSet")]
public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(ModelMetadata metadata, string expectedMemberName) public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(ModelMetadata metadata,
string expectedMemberName)
{ {
// Arrange // Arrange
var attribute = new Mock<ValidationAttribute> { CallBase = true }; var attribute = new Mock<ValidationAttribute> { CallBase = true };
@ -120,7 +121,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
attribute.Protected() attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>()) .Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(ValidationResult.Success); .Returns(ValidationResult.Success);
DataAnnotationsModelValidator validator = new DataAnnotationsModelValidator(attribute.Object); var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata); var validationContext = CreateValidationContext(metadata);
// Act // Act
@ -212,14 +213,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ModelValidationContext(null, null, null, metadata, null); return new ModelValidationContext(null, null, null, metadata, null);
} }
class DerivedRequiredAttribute : RequiredAttribute private class DerivedRequiredAttribute : RequiredAttribute
{ {
} }
class SampleModel private class SampleModel
{ {
public string Name { get; set; } public string Name { get; set; }
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Empty(validators); Assert.Empty(validators);
} }
class ClassWithoutAttributes private class ClassWithoutAttributes
{ {
public int TheProperty { get; set; } public int TheProperty { get; set; }
} }
@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[DataContract] [DataContract]
class ClassWithDataMemberIsRequiredTrue private class ClassWithDataMemberIsRequiredTrue
{ {
[DataMember(IsRequired = true)] [DataMember(IsRequired = true)]
public int TheProperty { get; set; } public int TheProperty { get; set; }
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[DataContract] [DataContract]
class ClassWithDataMemberIsRequiredFalse private class ClassWithDataMemberIsRequiredFalse
{ {
[DataMember(IsRequired = false)] [DataMember(IsRequired = false)]
public int TheProperty { get; set; } public int TheProperty { get; set; }
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Empty(validators); Assert.Empty(validators);
} }
class ClassWithDataMemberIsRequiredTrueWithoutDataContract private class ClassWithDataMemberIsRequiredTrueWithoutDataContract
{ {
[DataMember(IsRequired = true)] [DataMember(IsRequired = true)]
public int TheProperty { get; set; } public int TheProperty { get; set; }

View File

@ -51,6 +51,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void CombineWith() public void CombineWith()
{ {
// Arrange // Arrange
var expected = new[]
{
"Validating parent1.",
"Validating parent2.",
"Validated parent1.",
"Validated parent2."
};
var log = new List<string>(); var log = new List<string>();
var allChildNodes = new[] var allChildNodes = new[]
@ -77,7 +84,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
parentNode1.Validate(context); parentNode1.Validate(context);
// Assert // Assert
Assert.Equal(new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }, log.ToArray()); Assert.Equal(expected, log);
Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray()); Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray());
} }
@ -131,10 +138,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// 4. OnValidated() // 4. OnValidated()
// Arrange // Arrange
var expected = new[]
{
"In OnValidating()",
"In LoggingValidatonAttribute.IsValid()",
"In IValidatableObject.Validate()",
"In OnValidated()"
};
var log = new List<string>(); var log = new List<string>();
var model = new LoggingValidatableObject(log); var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model); var modelMetadata = GetModelMetadata(model);
var childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty"); var provider = new EmptyModelMetadataProvider();
var childMetadata = provider.GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty");
var node = new ModelValidationNode(modelMetadata, "theKey"); var node = new ModelValidationNode(modelMetadata, "theKey");
node.Validating += (sender, e) => log.Add("In OnValidating()"); node.Validating += (sender, e) => log.Add("In OnValidating()");
node.Validated += (sender, e) => log.Add("In OnValidated()"); node.Validated += (sender, e) => log.Add("In OnValidated()");
@ -145,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
node.Validate(context); node.Validate(context);
// Assert // Assert
Assert.Equal(new[] { "In OnValidating()", "In LoggingValidatonAttribute.IsValid()", "In IValidatableObject.Validate()", "In OnValidated()" }, log.ToArray()); Assert.Equal(expected, log);
} }
[Fact] [Fact]
@ -154,10 +169,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Because a property validator fails, the model validator shouldn't run // Because a property validator fails, the model validator shouldn't run
// Arrange // Arrange
var expected = new[]
{
"In OnValidating()",
"In IValidatableObject.Validate()",
"In OnValidated()"
};
var log = new List<string>(); var log = new List<string>();
var model = new LoggingValidatableObject(log); var model = new LoggingValidatableObject(log);
var modelMetadata = GetModelMetadata(model); var modelMetadata = GetModelMetadata(model);
var childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "InvalidStringProperty"); var provider = new EmptyModelMetadataProvider();
var childMetadata = provider.GetMetadataForProperty(() => model,
model.GetType(),
"InvalidStringProperty");
var node = new ModelValidationNode(modelMetadata, "theKey"); var node = new ModelValidationNode(modelMetadata, "theKey");
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty")); node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty"));
node.Validating += (sender, e) => log.Add("In OnValidating()"); node.Validating += (sender, e) => log.Add("In OnValidating()");
@ -168,8 +192,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
node.Validate(context); node.Validate(context);
// Assert // Assert
Assert.Equal(new[] { "In OnValidating()", "In IValidatableObject.Validate()", "In OnValidated()" }, log.ToArray()); Assert.Equal(expected, log);
Assert.Equal("Sample error message", context.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage); Assert.Equal("Sample error message",
context.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage);
} }
[Fact] [Fact]
@ -243,8 +268,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert // Assert
Assert.False(context.ModelState.ContainsKey("theKey.RequiredString")); Assert.False(context.ModelState.ContainsKey("theKey.RequiredString"));
Assert.Equal("existing Error Text", context.ModelState["theKey.RequiredString.Dummy"].Errors[0].ErrorMessage); Assert.Equal("existing Error Text",
Assert.Equal("The field RangedInt must be between 10 and 30.", context.ModelState["theKey.RangedInt"].Errors[0].ErrorMessage); context.ModelState["theKey.RequiredString.Dummy"].Errors[0].ErrorMessage);
Assert.Equal("The field RangedInt must be between 10 and 30.",
context.ModelState["theKey.RangedInt"].Errors[0].ErrorMessage);
Assert.False(context.ModelState.ContainsKey("theKey.ValidString")); Assert.False(context.ModelState.ContainsKey("theKey.ValidString"));
Assert.False(context.ModelState.ContainsKey("theKey")); Assert.False(context.ModelState.ContainsKey("theKey"));
} }
@ -261,7 +288,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static ModelValidationContext CreateContext(ModelMetadata metadata = null) private static ModelValidationContext CreateContext(ModelMetadata metadata = null)
{ {
var providers = new IModelValidatorProvider[] { var providers = new IModelValidatorProvider[]
{
new DataAnnotationsModelValidatorProvider(), new DataAnnotationsModelValidatorProvider(),
new DataMemberModelValidatorProvider() new DataMemberModelValidatorProvider()
}; };
@ -315,4 +343,4 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public string ValidString { get; set; } public string ValidString { get; set; }
} }
} }
} }

View File

@ -16,13 +16,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
private static readonly IReadableStringCollection _backingStore = new ReadableStringCollection( private static readonly IReadableStringCollection _backingStore = new ReadableStringCollection(
new Dictionary<string, string[]> new Dictionary<string, string[]>
{ {
{"foo", new[] { "fooValue1", "fooValue2"} }, { "foo", new[] { "fooValue1", "fooValue2"} },
{"bar.baz", new[] {"someOtherValue" }}, { "bar.baz", new[] {"someOtherValue" } },
{"null_value", null}, { "null_value", null },
{"prefix.null_value", null} { "prefix.null_value", null }
}); });
[Fact] [Fact]
public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix() public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
{ {
@ -79,15 +78,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes() public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
{ {
// Arrange // Arrange
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null); var expected = new Dictionary<string, string>
{
{ "bar", "bar" },
{ "foo", "foo" },
{ "null_value", "null_value" },
{ "prefix", "prefix" }
};
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, culture: null);
// Act // Act
var result = await valueProvider.GetKeysFromPrefixAsync(""); var result = await valueProvider.GetKeysFromPrefixAsync("");
// Assert // Assert
Assert.Equal<KeyValuePair<string, string>>( Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
result.OrderBy(kvp => kvp.Key),
new Dictionary<string, string> { { "bar", "bar" }, { "foo", "foo" }, { "null_value", "null_value" }, { "prefix", "prefix" } });
} }
[Fact] [Fact]
@ -147,7 +151,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Assert // Assert
Assert.NotNull(vpResult); Assert.NotNull(vpResult);
Assert.Equal(new [] { "fooValue1", "fooValue2" }, (IList<string>)vpResult.RawValue); Assert.Equal(new[] { "fooValue1", "fooValue2" }, (IList<string>)vpResult.RawValue);
Assert.Equal("fooValue1,fooValue2", vpResult.AttemptedValue); Assert.Equal("fooValue1,fooValue2", vpResult.AttemptedValue);
Assert.Equal(culture, vpResult.Culture); Assert.Equal(culture, vpResult.Culture);
} }
@ -179,7 +183,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Arrange // Arrange
var backingStore = new ReadableStringCollection( var backingStore = new ReadableStringCollection(
new Dictionary<string, string[]> new Dictionary<string, string[]>
{ {
{ "key", new string[] { null, null, "value" } } { "key", new string[] { null, null, "value" } }
}); });
var culture = new CultureInfo("fr-FR"); var culture = new CultureInfo("fr-FR");

View File

@ -145,7 +145,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsNullIfTrimmedValueIsEmptyString() public void ConvertToReturnsNullIfTrimmedValueIsEmptyString()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(rawValue: null, attemptedValue: null, culture: CultureInfo.InvariantCulture); var vpr = new ValueProviderResult(rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int[])); var outValue = vpr.ConvertTo(typeof(int[]));
@ -285,8 +287,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Theory] [Theory]
[InlineData(new object[] { new[] { 1, 0 }})] [InlineData(new object[] { new[] { 1, 0 } })]
[InlineData(new object[] { new[] {"Value1", "Value0" }})] [InlineData(new object[] { new[] { "Value1", "Value0" } })]
public void ConvertTo_ConvertsEnumArrays(object value) public void ConvertTo_ConvertsEnumArrays(object value)
{ {
// Arrange // Arrange
@ -395,8 +397,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
yield return new object[] { (double)42.0, 42 }; yield return new object[] { (double)42.0, 42 };
yield return new object[] { "2008-01-01", new DateTime(2008, 01, 01) }; yield return new object[] { "2008-01-01", new DateTime(2008, 01, 01) };
yield return new object[] { "00:00:20", TimeSpan.FromSeconds(20) }; yield return new object[] { "00:00:20", TimeSpan.FromSeconds(20) };
yield return new object[] { "c6687d3a-51f9-4159-8771-a66d2b7d7038", yield return new object[]
Guid.Parse("c6687d3a-51f9-4159-8771-a66d2b7d7038") }; {
"c6687d3a-51f9-4159-8771-a66d2b7d7038",
Guid.Parse("c6687d3a-51f9-4159-8771-a66d2b7d7038")
};
} }
} }