Reintroduce model validation
This commit is contained in:
parent
cca3f92c5f
commit
e434918337
|
|
@ -1,7 +1,11 @@
|
|||
namespace MvcSample.Models
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MvcSample.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
[Required]
|
||||
[MinLength(4)]
|
||||
public string Name { get; set; }
|
||||
public string Address { get; set; }
|
||||
public int Age { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,40 +1,42 @@
|
|||
{
|
||||
"version" : "0.1-alpha-*",
|
||||
"version": "0.1-alpha-*",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Routing" : "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Mvc.ModelBinding" : "",
|
||||
"Microsoft.AspNet.Mvc.Core" : "",
|
||||
"Microsoft.AspNet.Mvc" : "",
|
||||
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Routing": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Mvc.ModelBinding": "",
|
||||
"Microsoft.AspNet.Mvc.Core": "",
|
||||
"Microsoft.AspNet.Mvc": "",
|
||||
"Microsoft.AspNet.Mvc.Razor": "",
|
||||
"Microsoft.AspNet.Mvc.Rendering" : ""
|
||||
"Microsoft.AspNet.Mvc.Rendering": ""
|
||||
},
|
||||
"configurations": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"Autofac": "3.3.0",
|
||||
"Owin": "1.0",
|
||||
"Microsoft.AspNet.DependencyInjection.Autofac": "0.1-alpha-*",
|
||||
"Microsoft.Owin": "2.1.0",
|
||||
"Microsoft.Owin.Diagnostics": "2.1.0",
|
||||
"Microsoft.Owin.Hosting": "2.1.0",
|
||||
"Microsoft.Owin.Host.HttpListener": "2.1.0",
|
||||
"Microsoft.AspNet.AppBuilderSupport": "0.1-alpha-*"
|
||||
}
|
||||
"Autofac": "3.3.0",
|
||||
"Owin": "1.0",
|
||||
"Microsoft.AspNet.DependencyInjection.Autofac": "0.1-alpha-*",
|
||||
"Microsoft.Owin": "2.1.0",
|
||||
"Microsoft.Owin.Diagnostics": "2.1.0",
|
||||
"Microsoft.Owin.Hosting": "2.1.0",
|
||||
"Microsoft.Owin.Host.HttpListener": "2.1.0",
|
||||
"Microsoft.AspNet.AppBuilderSupport": "0.1-alpha-*",
|
||||
"System.ComponentModel.DataAnnotations": ""
|
||||
}
|
||||
},
|
||||
"k10" : {
|
||||
"dependencies": {
|
||||
"System.ComponentModel": "4.0.0.0",
|
||||
"System.Console": "4.0.0.0",
|
||||
"System.Diagnostics.Debug": "4.0.10.0",
|
||||
"System.Diagnostics.Tools": "4.0.0.0",
|
||||
"System.Dynamic.Runtime": "4.0.0.0",
|
||||
"System.Runtime": "4.0.20.0",
|
||||
"System.Runtime.InteropServices": "4.0.10.0",
|
||||
"System.Threading.Tasks": "4.0.0.0"
|
||||
}
|
||||
"k10": {
|
||||
"dependencies": {
|
||||
"System.ComponentModel": "4.0.0.0",
|
||||
"System.Console": "4.0.0.0",
|
||||
"System.Diagnostics.Debug": "4.0.10.0",
|
||||
"System.Diagnostics.Tools": "4.0.0.0",
|
||||
"System.Dynamic.Runtime": "4.0.0.0",
|
||||
"System.Runtime": "4.0.20.0",
|
||||
"System.Runtime.InteropServices": "4.0.10.0",
|
||||
"System.Threading.Tasks": "4.0.0.0",
|
||||
"Microsoft.ComponentModel.DataAnnotations": "0.1-alpha-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.Internal
|
|||
ModelMetadata = modelMetadata,
|
||||
ModelBinder = actionBindingContext.ModelBinder,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
ValidatorProviders = actionBindingContext.ValidatorProviders,
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
FallbackToEmptyPrefix = true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
|
|
@ -8,13 +9,15 @@ namespace Microsoft.AspNet.Mvc
|
|||
IModelMetadataProvider metadataProvider,
|
||||
IModelBinder modelBinder,
|
||||
IValueProvider valueProvider,
|
||||
IInputFormatter inputFormatter)
|
||||
IInputFormatter inputFormatter,
|
||||
IEnumerable<IModelValidatorProvider> validatorProviders)
|
||||
{
|
||||
ActionContext = context;
|
||||
MetadataProvider = metadataProvider;
|
||||
ModelBinder = modelBinder;
|
||||
ValueProvider = valueProvider;
|
||||
InputFormatter = inputFormatter;
|
||||
ValidatorProviders = validatorProviders;
|
||||
}
|
||||
|
||||
public ActionContext ActionContext { get; private set; }
|
||||
|
|
@ -26,5 +29,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public IValueProvider ValueProvider { get; private set; }
|
||||
|
||||
public IInputFormatter InputFormatter { get; private set; }
|
||||
|
||||
public IEnumerable<IModelValidatorProvider> ValidatorProviders { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,17 +10,20 @@ namespace Microsoft.AspNet.Mvc
|
|||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||
private readonly IEnumerable<IModelBinder> _modelBinders;
|
||||
private readonly IEnumerable<IValueProviderFactory> _valueProviderFactories;
|
||||
private readonly IEnumerable<IInputFormatter> _bodyReaders;
|
||||
private readonly IEnumerable<IInputFormatter> _inputFormatters;
|
||||
private readonly IEnumerable<IModelValidatorProvider> _validatorProviders;
|
||||
|
||||
public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider,
|
||||
IEnumerable<IModelBinder> modelBinders,
|
||||
IEnumerable<IValueProviderFactory> valueProviderFactories,
|
||||
IEnumerable<IInputFormatter> bodyReaders)
|
||||
IEnumerable<IInputFormatter> inputFormatters,
|
||||
IEnumerable<IModelValidatorProvider> validatorProviders)
|
||||
{
|
||||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_modelBinders = modelBinders.OrderBy(binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0);
|
||||
_valueProviderFactories = valueProviderFactories;
|
||||
_bodyReaders = bodyReaders;
|
||||
_inputFormatters = inputFormatters;
|
||||
_validatorProviders = validatorProviders;
|
||||
}
|
||||
|
||||
public async Task<ActionBindingContext> GetActionBindingContextAsync(ActionContext actionContext)
|
||||
|
|
@ -35,7 +38,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
_modelMetadataProvider,
|
||||
new CompositeModelBinder(_modelBinders),
|
||||
new CompositeValueProvider(valueProviders),
|
||||
new CompositeInputFormatter(_bodyReaders)
|
||||
new CompositeInputFormatter(_inputFormatters),
|
||||
_validatorProviders
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,6 @@ namespace Microsoft.AspNet.Mvc
|
|||
actionBindingContext.ModelBinder.BindModel(modelBindingContext);
|
||||
parameterValues[parameter.Name] = modelBindingContext.Model;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return parameterValues;
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
if (bindingContext.ModelBinder.BindModel(innerBindingContext))
|
||||
{
|
||||
boundValue = innerBindingContext.Model;
|
||||
// TODO: validation
|
||||
// bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode);
|
||||
bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode);
|
||||
}
|
||||
boundCollection.Add(ModelBindingHelper.CastOrDefault<TElement>(boundValue));
|
||||
}
|
||||
|
|
@ -114,9 +113,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
didBind = true;
|
||||
boundValue = childBindingContext.Model;
|
||||
|
||||
// TODO: Validation
|
||||
// merge validation up
|
||||
// bindingContext.ValidationNode.ChildNodes.Add(childBindingContext.ValidationNode);
|
||||
bindingContext.ValidationNode.ChildNodes.Add(childBindingContext.ValidationNode);
|
||||
}
|
||||
|
||||
// infinite size collection stops on first bind failure
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
if (bindingContext.ModelBinder.BindModel(propertyBindingContext))
|
||||
{
|
||||
dto.Results[propertyMetadata] = new ComplexModelDtoResult(propertyBindingContext.Model/*, propertyBindingContext.ValidationNode*/);
|
||||
dto.Results[propertyMetadata] = new ComplexModelDtoResult(propertyBindingContext.Model, propertyBindingContext.ValidationNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public sealed class ComplexModelDtoResult
|
||||
{
|
||||
public ComplexModelDtoResult(object model/*, ModelValidationNode validationNode*/)
|
||||
public ComplexModelDtoResult(object model,
|
||||
[NotNull] ModelValidationNode validationNode)
|
||||
{
|
||||
// TODO: Validation
|
||||
//if (validationNode == null)
|
||||
//{
|
||||
// throw Error.ArgumentNull("validationNode");
|
||||
//}
|
||||
|
||||
Model = model;
|
||||
//ValidationNode = validationNode;
|
||||
ValidationNode = validationNode;
|
||||
}
|
||||
|
||||
public object Model { get; private set; }
|
||||
|
||||
//public ModelValidationNode ValidationNode { get; private set; }
|
||||
public ModelValidationNode ValidationNode { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -29,14 +28,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
public virtual bool BindModel(ModelBindingContext bindingContext)
|
||||
{
|
||||
ModelBindingContext newBindingContext = CreateNewBindingContext(bindingContext, bindingContext.ModelName);
|
||||
var newBindingContext = CreateNewBindingContext(bindingContext,
|
||||
bindingContext.ModelName,
|
||||
reuseValidationNode: true);
|
||||
|
||||
bool boundSuccessfully = TryBind(newBindingContext);
|
||||
if (!boundSuccessfully && !String.IsNullOrEmpty(bindingContext.ModelName)
|
||||
if (!boundSuccessfully && !string.IsNullOrEmpty(bindingContext.ModelName)
|
||||
&& bindingContext.FallbackToEmptyPrefix)
|
||||
{
|
||||
// fallback to empty prefix?
|
||||
newBindingContext = CreateNewBindingContext(bindingContext, modelName: String.Empty);
|
||||
newBindingContext = CreateNewBindingContext(bindingContext,
|
||||
modelName: string.Empty,
|
||||
reuseValidationNode: false);
|
||||
boundSuccessfully = TryBind(newBindingContext);
|
||||
}
|
||||
|
||||
|
|
@ -49,31 +52,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// 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.
|
||||
// Complex types will reveal their individual properties as model names and do not require this.
|
||||
// TODO: Validation
|
||||
//if (!newBindingContext.ModelMetadata.IsComplexType && String.IsNullOrEmpty(newBindingContext.ModelName))
|
||||
//{
|
||||
// newBindingContext.ValidationNode = new Validation.ModelValidationNode(newBindingContext.ModelMetadata, bindingContext.ModelName);
|
||||
//}
|
||||
if (!newBindingContext.ModelMetadata.IsComplexType && String.IsNullOrEmpty(newBindingContext.ModelName))
|
||||
{
|
||||
newBindingContext.ValidationNode = new ModelValidationNode(newBindingContext.ModelMetadata, bindingContext.ModelName);
|
||||
}
|
||||
|
||||
//newBindingContext.ValidationNode.Validate(context, null /* parentNode */);
|
||||
var validationContext = new ModelValidationContext(bindingContext.ModelMetadata,
|
||||
bindingContext.ModelState,
|
||||
bindingContext.MetadataProvider,
|
||||
bindingContext.ValidatorProviders);
|
||||
|
||||
newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
|
||||
bindingContext.Model = newBindingContext.Model;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryBind(ModelBindingContext bindingContext)
|
||||
private bool TryBind([NotNull] ModelBindingContext bindingContext)
|
||||
{
|
||||
// TODO: The body of this method existed as HttpActionContextExtensions.Bind. We might have to refactor it into
|
||||
// something that is shared.
|
||||
if (bindingContext == null)
|
||||
{
|
||||
throw Error.ArgumentNull("bindingContext");
|
||||
}
|
||||
|
||||
// TODO: RuntimeHelpers.EnsureSufficientExecutionStack does not exist in the CoreCLR.
|
||||
// Protects against stack overflow for deeply nested model binding
|
||||
// RuntimeHelpers.EnsureSufficientExecutionStack();
|
||||
|
||||
foreach (IModelBinder binder in Binders)
|
||||
foreach (var binder in Binders)
|
||||
{
|
||||
if (binder.BindModel(bindingContext))
|
||||
{
|
||||
|
|
@ -85,7 +85,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return false;
|
||||
}
|
||||
|
||||
private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext, string modelName)
|
||||
private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext,
|
||||
string modelName,
|
||||
bool reuseValidationNode)
|
||||
{
|
||||
var newBindingContext = new ModelBindingContext
|
||||
{
|
||||
|
|
@ -93,17 +95,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelName = modelName,
|
||||
ModelState = oldBindingContext.ModelState,
|
||||
ValueProvider = oldBindingContext.ValueProvider,
|
||||
ValidatorProviders = oldBindingContext.ValidatorProviders,
|
||||
MetadataProvider = oldBindingContext.MetadataProvider,
|
||||
ModelBinder = oldBindingContext.ModelBinder,
|
||||
HttpContext = oldBindingContext.HttpContext
|
||||
};
|
||||
|
||||
// TODO: Validation
|
||||
//// validation is expensive to create, so copy it over if we can
|
||||
//if (Object.ReferenceEquals(modelName, oldBindingContext.ModelName))
|
||||
//{
|
||||
// newBindingContext.ValidationNode = oldBindingContext.ValidationNode;
|
||||
//}
|
||||
// validation is expensive to create, so copy it over if we can
|
||||
if (reuseValidationNode)
|
||||
{
|
||||
newBindingContext.ValidationNode = oldBindingContext.ValidationNode;
|
||||
}
|
||||
|
||||
return newBindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
object untypedModel = propertyBindingContext.Model;
|
||||
model = ModelBindingHelper.CastOrDefault<TModel>(untypedModel);
|
||||
// TODO: Revive once we get validation
|
||||
// parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode);
|
||||
parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
EnsureModel(bindingContext);
|
||||
IEnumerable<ModelMetadata> propertyMetadatas = GetMetadataForProperties(bindingContext);
|
||||
ComplexModelDto dto = CreateAndPopulateDto(bindingContext, propertyMetadatas);
|
||||
var propertyMetadatas = GetMetadataForProperties(bindingContext);
|
||||
var dto = CreateAndPopulateDto(bindingContext, propertyMetadatas);
|
||||
|
||||
// post-processing, e.g. property setters and hooking up validation
|
||||
ProcessDto(bindingContext, dto);
|
||||
// TODO: Validation
|
||||
// bindingContext.ValidationNode.ValidateAllProperties = true; // complex models require full validation
|
||||
// complex models require full validation
|
||||
bindingContext.ValidationNode.ValidateAllProperties = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private static bool CanBindType(Type modelType)
|
||||
{
|
||||
// Simple types cannot use this binder
|
||||
bool isComplexType = !modelType.HasStringConverter();
|
||||
var isComplexType = !modelType.HasStringConverter();
|
||||
if (!isComplexType)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -87,8 +87,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private ComplexModelDto CreateAndPopulateDto(ModelBindingContext bindingContext, IEnumerable<ModelMetadata> propertyMetadatas)
|
||||
{
|
||||
// create a DTO and call into the DTO binder
|
||||
ComplexModelDto originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
|
||||
ModelBindingContext dtoBindingContext = new ModelBindingContext(bindingContext)
|
||||
var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
|
||||
var dtoBindingContext = new ModelBindingContext(bindingContext)
|
||||
{
|
||||
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(() => originalDto, typeof(ComplexModelDto)),
|
||||
ModelName = bindingContext.ModelName
|
||||
|
|
@ -105,24 +105,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return Activator.CreateInstance(bindingContext.ModelType);
|
||||
}
|
||||
|
||||
//// 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)
|
||||
//{
|
||||
// return (sender, e) =>
|
||||
// {
|
||||
// ModelValidationNode validationNode = (ModelValidationNode)sender;
|
||||
// ModelStateDictionary modelState = e.ActionContext.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)
|
||||
{
|
||||
return (sender, e) =>
|
||||
{
|
||||
var validationNode = (ModelValidationNode)sender;
|
||||
var modelState = e.ValidationContext.ModelState;
|
||||
|
||||
// if (modelState.IsValidField(validationNode.ModelStateKey))
|
||||
// {
|
||||
// string errorMessage = ModelBinderConfig.ValueRequiredErrorMessageProvider(e.ActionContext, modelMetadata, incomingValue);
|
||||
// if (errorMessage != null)
|
||||
// {
|
||||
// modelState.AddModelError(validationNode.ModelStateKey, errorMessage);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//}
|
||||
if (modelState.IsValidField(validationNode.ModelStateKey))
|
||||
{
|
||||
// TODO: Revive ModelBinderConfig
|
||||
// string errorMessage = ModelBinderConfig.ValueRequiredErrorMessageProvider(e.ValidationContext, modelMetadata, incomingValue);
|
||||
var errorMessage = e.ValidationContext.ModelMetadata.PropertyName + " is required";
|
||||
if (errorMessage != null)
|
||||
{
|
||||
modelState.AddModelError(validationNode.ModelStateKey, errorMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual void EnsureModel(ModelBindingContext bindingContext)
|
||||
{
|
||||
|
|
@ -134,12 +136,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
protected virtual IEnumerable<ModelMetadata> GetMetadataForProperties(ModelBindingContext bindingContext)
|
||||
{
|
||||
// TODO: Revive required properties. This has a dependency on HttpBindingAttribute and DataAnnotations
|
||||
// keep a set of the required properties so that we can cross-reference bound properties later
|
||||
HashSet<string> requiredProperties = new HashSet<string>();
|
||||
// Dictionary<string, ModelValidator> requiredValidators;
|
||||
HashSet<string> skipProperties = new HashSet<string>();
|
||||
// GetRequiredPropertiesCollection(bindingContext, out requiredProperties, out skipProperties);
|
||||
HashSet<string> requiredProperties;
|
||||
Dictionary<string, IModelValidator> requiredValidators;
|
||||
HashSet<string> skipProperties;
|
||||
GetRequiredPropertiesCollection(bindingContext, out requiredProperties, out requiredValidators, out skipProperties);
|
||||
|
||||
return from propertyMetadata in bindingContext.ModelMetadata.Properties
|
||||
let propertyName = propertyMetadata.PropertyName
|
||||
|
|
@ -150,102 +151,84 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private static object GetPropertyDefaultValue(PropertyInfo propertyInfo)
|
||||
{
|
||||
DefaultValueAttribute attr = propertyInfo.GetCustomAttribute<DefaultValueAttribute>();
|
||||
var attr = propertyInfo.GetCustomAttribute<DefaultValueAttribute>();
|
||||
return (attr != null) ? attr.Value : null;
|
||||
}
|
||||
|
||||
//internal static void GetRequiredPropertiesCollection(ModelBindingContext bindingContext, out HashSet<string> requiredProperties, out HashSet<string> skipProperties)
|
||||
//{
|
||||
// requiredProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
// // requiredValidators = new Dictionary<string, ModelValidator>(StringComparer.OrdinalIgnoreCase);
|
||||
// skipProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
internal static void GetRequiredPropertiesCollection(ModelBindingContext bindingContext,
|
||||
out HashSet<string> requiredProperties,
|
||||
out Dictionary<string, IModelValidator> requiredValidators,
|
||||
out HashSet<string> skipProperties)
|
||||
{
|
||||
requiredProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
requiredValidators = new Dictionary<string, IModelValidator>(StringComparer.OrdinalIgnoreCase);
|
||||
skipProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// // Use attributes on the property before attributes on the type.
|
||||
// Type modelType = bindingContext.ModelType;
|
||||
// ICustomTypeDescriptor modelDescriptor = new AssociatedMetadataTypeTypeDescriptionProvider(modelType)
|
||||
// .GetTypeDescriptor(modelType);
|
||||
// TODO: HttpBindingBehaviorAttribute
|
||||
var modelTypeInfo = bindingContext.ModelType.GetTypeInfo();
|
||||
foreach (var propertyMetadata in bindingContext.ModelMetadata.Properties)
|
||||
{
|
||||
var propertyName = propertyMetadata.PropertyName;
|
||||
var requiredValidator = bindingContext.GetValidators(propertyMetadata)
|
||||
.FirstOrDefault(v => v.IsRequired);
|
||||
// TODO: Revive HttpBindingBehaviorAttribute
|
||||
|
||||
// PropertyDescriptorCollection propertyDescriptors = modelDescriptor.GetProperties();
|
||||
|
||||
// // TODO: Revive HttpBindingBehavior
|
||||
// // HttpBindingBehaviorAttribute typeAttr = modelDescriptor.GetAttributes().OfType<HttpBindingBehaviorAttribute>().SingleOrDefault();
|
||||
|
||||
// foreach (PropertyDescriptor propertyDescriptor in propertyDescriptors)
|
||||
// {
|
||||
// string propertyName = propertyDescriptor.Name;
|
||||
// ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyName];
|
||||
// // ModelValidator requiredValidator = context.GetValidators(propertyMetadata).Where(v => v.IsRequired).FirstOrDefault();
|
||||
// // requiredValidators[propertyName] = requiredValidator;
|
||||
|
||||
// HttpBindingBehaviorAttribute propAttr = propertyDescriptor.Attributes.OfType<HttpBindingBehaviorAttribute>().SingleOrDefault();
|
||||
// HttpBindingBehaviorAttribute workingAttr = propAttr ?? typeAttr;
|
||||
// if (workingAttr != null)
|
||||
// {
|
||||
// switch (workingAttr.Behavior)
|
||||
// {
|
||||
// case HttpBindingBehavior.Required:
|
||||
// requiredProperties.Add(propertyName);
|
||||
// break;
|
||||
|
||||
// case HttpBindingBehavior.Never:
|
||||
// skipProperties.Add(propertyName);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// else if (requiredValidator != null)
|
||||
// {
|
||||
// requiredProperties.Add(propertyName);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
if (requiredValidator != null)
|
||||
{
|
||||
requiredValidators[propertyName] = requiredValidator;
|
||||
requiredProperties.Add(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
|
||||
{
|
||||
// TODO: Uncomment this once we revive validation
|
||||
HashSet<string> requiredProperties;
|
||||
Dictionary<string, IModelValidator> requiredValidators;
|
||||
HashSet<string> skipProperties;
|
||||
GetRequiredPropertiesCollection(bindingContext, out requiredProperties, out requiredValidators, out skipProperties);
|
||||
|
||||
//HashSet<string> requiredProperties;
|
||||
//// Dictionary<string, ModelValidator> requiredValidators;
|
||||
//HashSet<string> skipProperties;
|
||||
//GetRequiredPropertiesCollection(context, bindingContext, out requiredProperties, out requiredValidators, out skipProperties);
|
||||
// Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
|
||||
requiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName));
|
||||
|
||||
//// Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
|
||||
//requiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName));
|
||||
foreach (var missingRequiredProperty in requiredProperties)
|
||||
{
|
||||
var addedError = false;
|
||||
var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
|
||||
bindingContext.ValidationNode.ModelStateKey, missingRequiredProperty);
|
||||
|
||||
//foreach (string missingRequiredProperty in requiredProperties)
|
||||
//{
|
||||
// string modelStateKey = ModelBindingHelper.CreatePropertyModelName(
|
||||
// bindingContext.ValidationNode.ModelStateKey, missingRequiredProperty);
|
||||
// Update Model as SetProperty() would: Place null value where validator will check for non-null. This
|
||||
// ensures a failure result from a required validator (if any) even for a non-nullable property.
|
||||
// (Otherwise, propertyMetadata.Model is likely already null.)
|
||||
var propertyMetadata = bindingContext.PropertyMetadata[missingRequiredProperty];
|
||||
propertyMetadata.Model = null;
|
||||
|
||||
// // Update Model as SetProperty() would: Place null value where validator will check for non-null. This
|
||||
// // ensures a failure result from a required validator (if any) even for a non-nullable property.
|
||||
// // (Otherwise, propertyMetadata.Model is likely already null.)
|
||||
// ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[missingRequiredProperty];
|
||||
// propertyMetadata.Model = null;
|
||||
// Execute validator (if any) to get custom error message.
|
||||
IModelValidator validator;
|
||||
if (requiredValidators.TryGetValue(missingRequiredProperty, out validator))
|
||||
{
|
||||
addedError = RunValidator(validator, bindingContext, propertyMetadata, modelStateKey);
|
||||
}
|
||||
|
||||
// // Execute validator (if any) to get custom error message.
|
||||
// ModelValidator validator = requiredValidators[missingRequiredProperty];
|
||||
// bool addedError = RunValidator(validator, bindingContext, propertyMetadata, modelStateKey);
|
||||
|
||||
// // Fall back to default message if HttpBindingBehaviorAttribute required this property or validator
|
||||
// // (oddly) succeeded.
|
||||
// if (!addedError)
|
||||
// {
|
||||
// bindingContext.ModelState.AddModelError(modelStateKey,
|
||||
// Error.Format(SRResources.MissingRequiredMember, missingRequiredProperty));
|
||||
// }
|
||||
//}
|
||||
// Fall back to default message if HttpBindingBehaviorAttribute required this property or validator
|
||||
// (oddly) succeeded.
|
||||
if (!addedError)
|
||||
{
|
||||
bindingContext.ModelState.AddModelError(
|
||||
modelStateKey,
|
||||
Resources.FormatMissingRequiredMember(missingRequiredProperty));
|
||||
}
|
||||
}
|
||||
|
||||
// for each property that was bound, call the setter, recording exceptions as necessary
|
||||
foreach (var entry in dto.Results)
|
||||
{
|
||||
ModelMetadata propertyMetadata = entry.Key;
|
||||
|
||||
ComplexModelDtoResult dtoResult = entry.Value;
|
||||
var propertyMetadata = entry.Key;
|
||||
var dtoResult = entry.Value;
|
||||
if (dtoResult != null)
|
||||
{
|
||||
SetProperty(bindingContext, propertyMetadata, dtoResult);
|
||||
// TODO: Validation
|
||||
// bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode);
|
||||
bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,14 +236,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
[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,
|
||||
ModelMetadata propertyMetadata,
|
||||
ComplexModelDtoResult dtoResult
|
||||
/*, ModelValidator requiredValidator*/)
|
||||
ComplexModelDtoResult dtoResult)
|
||||
{
|
||||
// TODO: This used TypeDescriptor which is no longer available. Lookups performed using System.ComponentModel were
|
||||
// cached. To maintain parity, we'll need to cache property lookups.
|
||||
PropertyInfo property = bindingContext.ModelType
|
||||
.GetRuntimeProperties()
|
||||
.FirstOrDefault(p => p.Name.Equals(propertyMetadata.PropertyName, StringComparison.OrdinalIgnoreCase));
|
||||
var property = bindingContext.ModelType
|
||||
.GetProperty(propertyMetadata.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
|
||||
|
||||
if (property == null || !property.CanWrite)
|
||||
{
|
||||
|
|
@ -268,21 +247,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return;
|
||||
}
|
||||
|
||||
object value = dtoResult.Model ?? GetPropertyDefaultValue(property);
|
||||
var value = dtoResult.Model ?? GetPropertyDefaultValue(property);
|
||||
propertyMetadata.Model = value;
|
||||
|
||||
|
||||
//// '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. See comments in
|
||||
//// DefaultModelBinder.SetProperty() for more information.
|
||||
//if (value == null)
|
||||
//{
|
||||
// string modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
// if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
// {
|
||||
// RunValidator(requiredValidator, bindingContext, propertyMetadata, modelStateKey);
|
||||
// }
|
||||
//}
|
||||
// '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.
|
||||
if (value == null)
|
||||
{
|
||||
var modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
{
|
||||
var requiredValidator = bindingContext.GetValidators(propertyMetadata).FirstOrDefault(v => v.IsRequired);
|
||||
if (requiredValidator != null)
|
||||
{
|
||||
var validationContext = bindingContext.CreateValidationContext(propertyMetadata);
|
||||
foreach (var validationResult in requiredValidator.Validate(validationContext))
|
||||
{
|
||||
bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null || property.PropertyType.AllowsNullValue())
|
||||
{
|
||||
|
|
@ -293,39 +279,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
catch (Exception ex)
|
||||
{
|
||||
// don't display a duplicate error message if a binding error has already occurred for this field
|
||||
//string modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
//if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
//{
|
||||
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
|
||||
//}
|
||||
var modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
{
|
||||
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// trying to set a non-nullable value type to null, need to make sure there's a message
|
||||
//string modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
//if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
//{
|
||||
// dtoResult.ValidationNode.Validated += CreateNullCheckFailedHandler(propertyMetadata, value);
|
||||
//}
|
||||
var modelStateKey = dtoResult.ValidationNode.ModelStateKey;
|
||||
if (bindingContext.ModelState.IsValidField(modelStateKey))
|
||||
{
|
||||
dtoResult.ValidationNode.Validated += CreateNullCheckFailedHandler(propertyMetadata, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// Returns true if validator execution adds a model error.
|
||||
//private static bool RunValidator(ModelValidator validator, ModelBindingContext bindingContext,
|
||||
// ModelMetadata propertyMetadata, string modelStateKey)
|
||||
//{
|
||||
// bool addedError = false;
|
||||
// if (validator != null)
|
||||
// {
|
||||
// foreach (ModelValidationResult validationResult in validator.Validate(propertyMetadata, bindingContext.Model))
|
||||
// {
|
||||
// bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message);
|
||||
// addedError = true;
|
||||
// }
|
||||
// }
|
||||
// Returns true if validator execution adds a model error.
|
||||
private static bool RunValidator(IModelValidator validator,
|
||||
ModelBindingContext bindingContext,
|
||||
ModelMetadata propertyMetadata,
|
||||
string modelStateKey)
|
||||
{
|
||||
var validationContext = bindingContext.CreateValidationContext(propertyMetadata);
|
||||
|
||||
// return addedError;
|
||||
//}
|
||||
var addedError = false;
|
||||
foreach (var validationResult in validator.Validate(validationContext))
|
||||
{
|
||||
bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message);
|
||||
addedError = true;
|
||||
}
|
||||
return addedError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -17,17 +18,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
object model = valueProviderResult.RawValue;
|
||||
ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model);
|
||||
bindingContext.Model = model;
|
||||
if (bindingContext.ModelMetadata.IsComplexType)
|
||||
{
|
||||
// TODO: Validation
|
||||
//IBodyModelValidator validator = services.GetBodyModelValidator();
|
||||
//ModelMetadataProvider metadataProvider = services.GetModelMetadataProvider();
|
||||
//if (validator != null && metadataProvider != null)
|
||||
//{
|
||||
// validator.Validate(model, bindingContext.ModelType, metadataProvider, context, bindingContext.ModelName);
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Determine if we need IBodyValidator here.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
|
||||
{
|
||||
public static class DictionaryHelper
|
||||
{
|
||||
public static IEnumerable<KeyValuePair<string, TValue>> FindKeysWithPrefix<TValue>([NotNull] IDictionary<string, TValue> dictionary,
|
||||
[NotNull] string prefix)
|
||||
{
|
||||
TValue exactMatchValue;
|
||||
if (dictionary.TryGetValue(prefix, out exactMatchValue))
|
||||
{
|
||||
yield return new KeyValuePair<string, TValue>(prefix, exactMatchValue);
|
||||
}
|
||||
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
var key = entry.Key;
|
||||
|
||||
if (key.Length <= prefix.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything is prefixed by the empty string
|
||||
if (prefix.Length == 0)
|
||||
{
|
||||
yield return entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
char charAfterPrefix = key[prefix.Length];
|
||||
switch (charAfterPrefix)
|
||||
{
|
||||
case '[':
|
||||
case '.':
|
||||
yield return entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
|
||||
{
|
||||
internal class EfficientTypePropertyKey<T1, T2> : Tuple<T1, T2>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
|
||||
{
|
||||
internal static class ModelBindingContextExtensions
|
||||
{
|
||||
public static IEnumerable<IModelValidator> GetValidators([NotNull] this ModelBindingContext context,
|
||||
[NotNull] ModelMetadata metadata)
|
||||
{
|
||||
return context.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata))
|
||||
.Where(v => v != null);
|
||||
}
|
||||
|
||||
public static ModelValidationContext CreateValidationContext([NotNull] this ModelBindingContext context,
|
||||
[NotNull] ModelMetadata metadata)
|
||||
{
|
||||
return new ModelValidationContext(metadata,
|
||||
context.ModelState,
|
||||
context.MetadataProvider,
|
||||
context.ValidatorProviders);
|
||||
}
|
||||
|
||||
public static IEnumerable<ModelValidationResult> Validate([NotNull] this ModelBindingContext bindingContext)
|
||||
{
|
||||
var validators = GetValidators(bindingContext, bindingContext.ModelMetadata);
|
||||
var compositeValidator = new CompositeModelValidator(validators);
|
||||
var modelValidationContext = CreateValidationContext(bindingContext, bindingContext.ModelMetadata);
|
||||
return compositeValidator.Validate(modelValidationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,28 +14,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
private readonly ConcurrentDictionary<Type, TypeInformation> _typeInfoCache = new ConcurrentDictionary<Type, TypeInformation>();
|
||||
|
||||
public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
|
||||
public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType)
|
||||
{
|
||||
if (containerType == null)
|
||||
{
|
||||
throw Error.ArgumentNull("containerType");
|
||||
}
|
||||
|
||||
return GetMetadataForPropertiesCore(container, containerType);
|
||||
}
|
||||
|
||||
public ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
|
||||
public ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, [NotNull] Type containerType, [NotNull] string propertyName)
|
||||
{
|
||||
if (containerType == null)
|
||||
{
|
||||
throw Error.ArgumentNull("containerType");
|
||||
}
|
||||
if (String.IsNullOrEmpty(propertyName))
|
||||
if (string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
throw Error.ArgumentNullOrEmpty("propertyName");
|
||||
}
|
||||
|
||||
TypeInformation typeInfo = GetTypeInformation(containerType);
|
||||
var typeInfo = GetTypeInformation(containerType);
|
||||
PropertyInformation propertyInfo;
|
||||
if (!typeInfo.Properties.TryGetValue(propertyName, out propertyInfo))
|
||||
{
|
||||
|
|
@ -45,13 +36,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return CreateMetadataFromPrototype(propertyInfo.Prototype, modelAccessor);
|
||||
}
|
||||
|
||||
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType)
|
||||
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType)
|
||||
{
|
||||
if (modelType == null)
|
||||
{
|
||||
throw Error.ArgumentNull("modelType");
|
||||
}
|
||||
|
||||
TModelMetadata prototype = GetTypeInformation(modelType).Prototype;
|
||||
return CreateMetadataFromPrototype(prototype, modelAccessor);
|
||||
}
|
||||
|
|
@ -68,10 +54,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
|
||||
{
|
||||
TypeInformation typeInfo = GetTypeInformation(containerType);
|
||||
foreach (KeyValuePair<string, PropertyInformation> kvp in typeInfo.Properties)
|
||||
var typeInfo = GetTypeInformation(containerType);
|
||||
foreach (var kvp in typeInfo.Properties)
|
||||
{
|
||||
PropertyInformation propertyInfo = kvp.Value;
|
||||
var propertyInfo = kvp.Value;
|
||||
Func<object> modelAccessor = null;
|
||||
if (container != null)
|
||||
{
|
||||
|
|
@ -96,24 +82,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private TypeInformation CreateTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes)
|
||||
{
|
||||
TypeInfo typeInfo = type.GetTypeInfo();
|
||||
IEnumerable<Attribute> attributes = typeInfo.GetCustomAttributes();
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var attributes = typeInfo.GetCustomAttributes();
|
||||
if (associatedAttributes != null)
|
||||
{
|
||||
attributes = attributes.Concat(associatedAttributes);
|
||||
}
|
||||
TypeInformation info = new TypeInformation
|
||||
var info = new TypeInformation
|
||||
{
|
||||
Prototype = CreateMetadataPrototype(attributes, containerType: null, modelType: type, propertyName: null)
|
||||
};
|
||||
// TODO: Determine if we need this. TypeDescriptor does not exist in CoreCLR.
|
||||
//ICustomTypeDescriptor typeDescriptor = TypeDescriptorHelper.Get(type);
|
||||
//info.TypeDescriptor = typeDescriptor;
|
||||
|
||||
Dictionary<string, PropertyInformation> properties = new Dictionary<string, PropertyInformation>();
|
||||
|
||||
// TODO: Figure out if there's a better way to identify public non-static properties
|
||||
foreach (PropertyInfo property in type.GetRuntimeProperties().Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic))
|
||||
var properties = new Dictionary<string, PropertyInformation>();
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
// Avoid re-generating a property descriptor if one has already been generated for the property name
|
||||
if (!properties.ContainsKey(property.Name))
|
||||
|
|
@ -128,21 +109,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private PropertyInformation CreatePropertyInformation(Type containerType, PropertyInfo property)
|
||||
{
|
||||
PropertyInformation info = new PropertyInformation();
|
||||
var info = new PropertyInformation();
|
||||
info.ValueAccessor = CreatePropertyValueAccessor(property);
|
||||
info.Prototype = CreateMetadataPrototype(property.GetCustomAttributes().Cast<Attribute>(), containerType, property.PropertyType, property.Name);
|
||||
info.Prototype = CreateMetadataPrototype(property.GetCustomAttributes(),
|
||||
containerType,
|
||||
property.PropertyType,
|
||||
property.Name);
|
||||
return info;
|
||||
}
|
||||
|
||||
private static Func<object, object> CreatePropertyValueAccessor(PropertyInfo property)
|
||||
{
|
||||
Type declaringType = property.DeclaringType;
|
||||
TypeInfo declaringTypeInfo = declaringType.GetTypeInfo();
|
||||
var declaringType = property.DeclaringType;
|
||||
var declaringTypeInfo = declaringType.GetTypeInfo();
|
||||
if (declaringTypeInfo.IsVisible)
|
||||
{
|
||||
if (property.CanRead)
|
||||
{
|
||||
MethodInfo getMethodInfo = property.GetMethod;
|
||||
var getMethodInfo = property.GetMethod;
|
||||
if (getMethodInfo != null)
|
||||
{
|
||||
return CreateDynamicValueAccessor(getMethodInfo, declaringType, property.Name);
|
||||
|
|
@ -161,10 +145,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
Contract.Assert(getMethodInfo != null && getMethodInfo.IsPublic && !getMethodInfo.IsStatic);
|
||||
|
||||
TypeInfo declaringTypeInfo = declaringType.GetTypeInfo();
|
||||
Type propertyType = getMethodInfo.ReturnType;
|
||||
DynamicMethod dynamicMethod = new DynamicMethod("Get" + propertyName + "From" + declaringType.Name, typeof(object), new Type[] { typeof(object) });
|
||||
ILGenerator ilg = dynamicMethod.GetILGenerator();
|
||||
var declaringTypeInfo = declaringType.GetTypeInfo();
|
||||
var propertyType = getMethodInfo.ReturnType;
|
||||
var dynamicMethod = new DynamicMethod("Get" + propertyName + "From" + declaringType.Name,
|
||||
typeof(object),
|
||||
new [] { typeof(object) });
|
||||
var ilg = dynamicMethod.GetILGenerator();
|
||||
|
||||
// Load the container onto the stack, convert from object => declaring type for the property
|
||||
ilg.Emit(OpCodes.Ldarg_0);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -9,6 +10,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
private string _modelName;
|
||||
private ModelStateDictionary _modelState;
|
||||
private Dictionary<string, ModelMetadata> _propertyMetadata;
|
||||
private ModelValidationNode _validationNode;
|
||||
|
||||
public ModelBindingContext()
|
||||
{
|
||||
|
|
@ -24,6 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ValueProvider = bindingContext.ValueProvider;
|
||||
MetadataProvider = bindingContext.MetadataProvider;
|
||||
ModelBinder = bindingContext.ModelBinder;
|
||||
ValidatorProviders = bindingContext.ValidatorProviders;
|
||||
HttpContext = bindingContext.HttpContext;
|
||||
}
|
||||
}
|
||||
|
|
@ -102,6 +106,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
set;
|
||||
}
|
||||
|
||||
public IEnumerable<IModelValidatorProvider> ValidatorProviders
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IDictionary<string, ModelMetadata> PropertyMetadata
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_propertyMetadata == null)
|
||||
{
|
||||
_propertyMetadata = ModelMetadata.Properties.ToDictionary(m => m.PropertyName, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return _propertyMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
public ModelValidationNode ValidationNode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_validationNode == null)
|
||||
{
|
||||
_validationNode = new ModelValidationNode(ModelMetadata, ModelName);
|
||||
}
|
||||
return _validationNode;
|
||||
}
|
||||
set { _validationNode = value; }
|
||||
}
|
||||
|
||||
private void EnsureModelMetadata()
|
||||
{
|
||||
if (ModelMetadata == null)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
}
|
||||
|
||||
public ModelStateDictionary([NotNull]ModelStateDictionary dictionary)
|
||||
public ModelStateDictionary([NotNull] ModelStateDictionary dictionary)
|
||||
{
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
|
|
@ -35,7 +36,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
GetModelStateForKey(key).Errors.Add(errorMessage);
|
||||
}
|
||||
|
||||
private ModelState GetModelStateForKey([NotNull]string key)
|
||||
public bool IsValidField([NotNull] string key)
|
||||
{
|
||||
// if the key is not found in the dictionary, we just say that it's valid (since there are no errors)
|
||||
foreach (var entry in DictionaryHelper.FindKeysWithPrefix(this, key))
|
||||
{
|
||||
if (entry.Value.Errors.Count != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ModelState GetModelStateForKey([NotNull] string key)
|
||||
{
|
||||
ModelState modelState;
|
||||
if (!TryGetValue(key, out modelState))
|
||||
|
|
|
|||
|
|
@ -58,6 +58,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return GetString("JQuerySyntaxMissingClosingBracket");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property '{0}' on type '{1}' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].
|
||||
/// </summary>
|
||||
internal static string MissingDataMemberIsRequired
|
||||
{
|
||||
get { return GetString("MissingDataMemberIsRequired"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property '{0}' on type '{1}' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].
|
||||
/// </summary>
|
||||
internal static string FormatMissingDataMemberIsRequired(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MissingDataMemberIsRequired"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' property is required.
|
||||
/// </summary>
|
||||
internal static string MissingRequiredMember
|
||||
{
|
||||
get { return GetString("MissingRequiredMember"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' property is required.
|
||||
/// </summary>
|
||||
internal static string FormatMissingRequiredMember(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MissingRequiredMember"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value '{0}' is not valid for {1}.
|
||||
/// </summary>
|
||||
|
|
@ -170,6 +202,70 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return GetString("ModelBindingContext_ModelMetadataMustBeSet");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
|
||||
/// </summary>
|
||||
internal static string ValidatableObjectAdapter_IncompatibleType
|
||||
{
|
||||
get { return GetString("ValidatableObjectAdapter_IncompatibleType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatValidatableObjectAdapter_IncompatibleType(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValidatableObjectAdapter_IncompatibleType"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on fields are not supported. Consider using a public property for validation instead.
|
||||
/// </summary>
|
||||
internal static string ValidationAttributeOnField
|
||||
{
|
||||
get { return GetString("ValidationAttributeOnField"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on fields are not supported. Consider using a public property for validation instead.
|
||||
/// </summary>
|
||||
internal static string FormatValidationAttributeOnField(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValidationAttributeOnField"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-public property '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on non-public properties are not supported. Consider using a public property for validation instead.
|
||||
/// </summary>
|
||||
internal static string ValidationAttributeOnNonPublicProperty
|
||||
{
|
||||
get { return GetString("ValidationAttributeOnNonPublicProperty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-public property '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on non-public properties are not supported. Consider using a public property for validation instead.
|
||||
/// </summary>
|
||||
internal static string FormatValidationAttributeOnNonPublicProperty(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValidationAttributeOnNonPublicProperty"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value is required but was not present in the request.
|
||||
/// </summary>
|
||||
internal static string Validation_ValueNotFound
|
||||
{
|
||||
get { return GetString("Validation_ValueNotFound"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value is required but was not present in the request.
|
||||
/// </summary>
|
||||
internal static string FormatValidation_ValueNotFound()
|
||||
{
|
||||
return GetString("Validation_ValueNotFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,12 @@
|
|||
<data name="JQuerySyntaxMissingClosingBracket" xml:space="preserve">
|
||||
<value>The key is invalid JQuery syntax because it is missing a closing bracket.</value>
|
||||
</data>
|
||||
<data name="MissingDataMemberIsRequired" xml:space="preserve">
|
||||
<value>Property '{0}' on type '{1}' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].</value>
|
||||
</data>
|
||||
<data name="MissingRequiredMember" xml:space="preserve">
|
||||
<value>The '{0}' property is required.</value>
|
||||
</data>
|
||||
<data name="ModelBinderConfig_ValueInvalid" xml:space="preserve">
|
||||
<value>The value '{0}' is not valid for {1}.</value>
|
||||
</data>
|
||||
|
|
@ -147,6 +153,18 @@
|
|||
<data name="ModelBindingContext_ModelMetadataMustBeSet" xml:space="preserve">
|
||||
<value>The ModelMetadata property must be set before accessing this property.</value>
|
||||
</data>
|
||||
<data name="ValidatableObjectAdapter_IncompatibleType" xml:space="preserve">
|
||||
<value>The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValidationAttributeOnField" xml:space="preserve">
|
||||
<value>Field '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on fields are not supported. Consider using a public property for validation instead.</value>
|
||||
</data>
|
||||
<data name="ValidationAttributeOnNonPublicProperty" xml:space="preserve">
|
||||
<value>Non-public property '{0}' on type '{1}' is attributed with one or more validation attributes. Validation attributes on non-public properties are not supported. Consider using a public property for validation instead.</value>
|
||||
</data>
|
||||
<data name="Validation_ValueNotFound" xml:space="preserve">
|
||||
<value>A value is required but was not present in the request.</value>
|
||||
</data>
|
||||
<data name="ValueProviderResult_ConversionThrew" xml:space="preserve">
|
||||
<value>The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public abstract class AssociatedValidatorProvider : IModelValidatorProvider
|
||||
{
|
||||
public IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata)
|
||||
{
|
||||
if (metadata == null)
|
||||
{
|
||||
throw new ArgumentNullException("metadata");
|
||||
}
|
||||
|
||||
if (metadata.ContainerType != null && !string.IsNullOrEmpty(metadata.PropertyName))
|
||||
{
|
||||
return GetValidatorsForProperty(metadata);
|
||||
}
|
||||
|
||||
return GetValidatorsForType(metadata);
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes);
|
||||
|
||||
private IEnumerable<IModelValidator> GetValidatorsForProperty(ModelMetadata metadata)
|
||||
{
|
||||
var propertyName = metadata.PropertyName;
|
||||
var property = metadata.ContainerType
|
||||
.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatCommon_PropertyNotFound(
|
||||
metadata.ContainerType.FullName,
|
||||
metadata.PropertyName),
|
||||
"metadata");
|
||||
}
|
||||
|
||||
var attributes = property.GetCustomAttributes();
|
||||
return GetValidators(metadata, attributes);
|
||||
}
|
||||
|
||||
private IEnumerable<IModelValidator> GetValidatorsForType(ModelMetadata metadata)
|
||||
{
|
||||
var attributes = metadata.ModelType
|
||||
.GetTypeInfo()
|
||||
.GetCustomAttributes();
|
||||
return GetValidators(metadata, attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class CompositeModelValidator : IModelValidator
|
||||
{
|
||||
private readonly IEnumerable<IModelValidator> _validators;
|
||||
|
||||
public CompositeModelValidator(IEnumerable<IModelValidator> validators)
|
||||
{
|
||||
_validators = validators;
|
||||
}
|
||||
|
||||
public bool IsRequired
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
var propertiesValid = true;
|
||||
var metadata = context.ModelMetadata;
|
||||
|
||||
foreach (var propertyMetadata in metadata.Properties)
|
||||
{
|
||||
var propertyContext = new ModelValidationContext(context, propertyMetadata);
|
||||
|
||||
foreach (var propertyValidator in _validators)
|
||||
{
|
||||
foreach (var validationResult in propertyValidator.Validate(propertyContext))
|
||||
{
|
||||
propertiesValid = false;
|
||||
yield return CreateSubPropertyResult(propertyMetadata, validationResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesValid)
|
||||
{
|
||||
foreach (var typeValidator in _validators)
|
||||
{
|
||||
foreach (var typeResult in typeValidator.Validate(context))
|
||||
{
|
||||
yield return typeResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ModelValidationResult CreateSubPropertyResult(ModelMetadata propertyMetadata, ModelValidationResult propertyResult)
|
||||
{
|
||||
return new ModelValidationResult(propertyMetadata.PropertyName + '.' + propertyResult.MemberName,
|
||||
propertyResult.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataAnnotationsModelValidator : IModelValidator
|
||||
{
|
||||
public DataAnnotationsModelValidator(ValidationAttribute attribute)
|
||||
{
|
||||
if (attribute == null)
|
||||
{
|
||||
throw new ArgumentNullException("attribute");
|
||||
}
|
||||
|
||||
Attribute = attribute;
|
||||
}
|
||||
|
||||
public ValidationAttribute Attribute { get; private set; }
|
||||
|
||||
public bool IsRequired
|
||||
{
|
||||
get { return Attribute is RequiredAttribute; }
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
|
||||
{
|
||||
var metadata = validationContext.ModelMetadata;
|
||||
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
||||
var context = new ValidationContext(metadata.Model)
|
||||
{
|
||||
DisplayName = metadata.GetDisplayName(),
|
||||
MemberName = memberName
|
||||
};
|
||||
|
||||
var result = Attribute.GetValidationResult(metadata.Model, context);
|
||||
if (result != ValidationResult.Success)
|
||||
{
|
||||
// ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to
|
||||
// construct the ModelKey for ModelStateDictionary. When validating at type level we want to append the
|
||||
// returned MemberNames if specified (e.g. person.Address.FirstName). For property validation, the
|
||||
// ModelKey can be constructed using the ModelMetadata and we should ignore MemberName (we don't want
|
||||
// (person.Name.Name). However the invoking validator does not have a way to distinguish between these two
|
||||
// cases. Consequently we'll only set MemberName if this validation returns a MemberName that is different
|
||||
// from the property being validated.
|
||||
|
||||
var errorMemberName = result.MemberNames.FirstOrDefault();
|
||||
if (string.Equals(errorMemberName, memberName, StringComparison.Ordinal))
|
||||
{
|
||||
errorMemberName = null;
|
||||
}
|
||||
|
||||
var validationResult = new ModelValidationResult(errorMemberName, result.ErrorMessage);
|
||||
return new ModelValidationResult[] { validationResult };
|
||||
}
|
||||
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="ModelValidatorProvider"/> which providers validators
|
||||
/// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides
|
||||
/// a validator for types which implement <see cref="IValidatableObject"/>. To support
|
||||
/// client side validation, you can either register adapters through the static methods
|
||||
/// on this class, or by having your validation attributes implement
|
||||
/// <see cref="IClientValidatable"/>. The logic to support IClientValidatable
|
||||
/// is implemented in <see cref="DataAnnotationsModelValidator"/>.
|
||||
/// </summary>
|
||||
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
// A factory for validators based on ValidationAttribute
|
||||
private delegate IModelValidator DataAnnotationsModelValidationFactory(ValidationAttribute attribute);
|
||||
|
||||
// A factory for validators based on IValidatableObject
|
||||
private delegate IModelValidator DataAnnotationsValidatableObjectAdapterFactory();
|
||||
|
||||
private static bool _addImplicitRequiredAttributeForValueTypes = true;
|
||||
|
||||
// Factories for validation attributes
|
||||
private static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
|
||||
(attribute) => new DataAnnotationsModelValidator(attribute);
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories =
|
||||
new Dictionary<Type, DataAnnotationsModelValidationFactory>();
|
||||
|
||||
#if NET45
|
||||
// Factories for IValidatableObject models
|
||||
private static DataAnnotationsValidatableObjectAdapterFactory DefaultValidatableFactory =
|
||||
() => new ValidatableObjectAdapter();
|
||||
#endif
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory> ValidatableFactories =
|
||||
new Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory>();
|
||||
|
||||
public static bool AddImplicitRequiredAttributeForValueTypes
|
||||
{
|
||||
get { return _addImplicitRequiredAttributeForValueTypes; }
|
||||
set { _addImplicitRequiredAttributeForValueTypes = value; }
|
||||
}
|
||||
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes)
|
||||
{
|
||||
var results = new List<IModelValidator>();
|
||||
|
||||
// Produce a validator for each validation attribute we find
|
||||
foreach (var attribute in attributes.OfType<ValidationAttribute>())
|
||||
{
|
||||
DataAnnotationsModelValidationFactory factory;
|
||||
if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
{
|
||||
factory = DefaultAttributeFactory;
|
||||
}
|
||||
results.Add(factory(attribute));
|
||||
}
|
||||
|
||||
#if NET45
|
||||
// Produce a validator if the type supports IValidatableObject
|
||||
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
|
||||
{
|
||||
DataAnnotationsValidatableObjectAdapterFactory factory;
|
||||
if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory))
|
||||
{
|
||||
factory = DefaultValidatableFactory;
|
||||
}
|
||||
results.Add(factory());
|
||||
}
|
||||
#endif
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="ModelValidatorProvider"/> provides a required ModelValidator for members marked as [DataMember(IsRequired=true)].
|
||||
/// </summary>
|
||||
public class DataMemberModelValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes)
|
||||
{
|
||||
// Types cannot be required; only properties can
|
||||
if (metadata.ContainerType == null || string.IsNullOrEmpty(metadata.PropertyName))
|
||||
{
|
||||
return Enumerable.Empty<IModelValidator>();
|
||||
}
|
||||
|
||||
if (IsRequiredDataMember(metadata.ContainerType, attributes))
|
||||
{
|
||||
return new[] { new RequiredMemberModelValidator() };
|
||||
}
|
||||
|
||||
return Enumerable.Empty<IModelValidator>();
|
||||
}
|
||||
|
||||
internal static bool IsRequiredDataMember(Type containerType, IEnumerable<Attribute> attributes)
|
||||
{
|
||||
var dataMemberAttribute = attributes.OfType<DataMemberAttribute>()
|
||||
.FirstOrDefault();
|
||||
if (dataMemberAttribute != null)
|
||||
{
|
||||
// isDataContract == true iff the container type has at least one DataContractAttribute
|
||||
bool isDataContract = containerType.GetTypeInfo()
|
||||
.GetCustomAttributes()
|
||||
.OfType<DataContractAttribute>()
|
||||
.Any();
|
||||
if (isDataContract && dataMemberAttribute.IsRequired)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="IModelValidator"/> to represent an error. This validator will always throw an exception regardless
|
||||
/// of the actual model value.
|
||||
/// This is used to perform meta-validation - that is to verify the validation attributes make sense.
|
||||
/// </summary>
|
||||
public class ErrorModelValidator : IModelValidator
|
||||
{
|
||||
private readonly string _errorMessage;
|
||||
|
||||
public ErrorModelValidator([NotNull] string errorMessage)
|
||||
{
|
||||
_errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public bool IsRequired { get { return false; } }
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
throw new InvalidOperationException(_errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public interface IModelValidator
|
||||
{
|
||||
bool IsRequired { get; }
|
||||
|
||||
IEnumerable<ModelValidationResult> Validate(ModelValidationContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public interface IModelValidatorProvider
|
||||
{
|
||||
IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class InvalidModelValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes)
|
||||
{
|
||||
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
|
||||
// Validation only runs against public properties
|
||||
var type = metadata.ModelType;
|
||||
var nonPublicProperties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
foreach (var nonPublicProperty in nonPublicProperties)
|
||||
{
|
||||
if (nonPublicProperty.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any())
|
||||
{
|
||||
yield return new ErrorModelValidator(Resources.FormatValidationAttributeOnNonPublicProperty(nonPublicProperty.Name, type));
|
||||
}
|
||||
}
|
||||
|
||||
var allFields = metadata.ModelType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
foreach (var field in allFields)
|
||||
{
|
||||
if (field.GetCustomAttributes(typeof(ValidationAttribute), inherit: true).Any())
|
||||
{
|
||||
yield return new ErrorModelValidator(Resources.FormatValidationAttributeOnField(field.Name, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validate that value-typed properties marked as [Required] are also marked as [DataMember(IsRequired=true)]
|
||||
// Certain formatters may not recognize a member as required if it's marked as [Required] but not [DataMember(IsRequired=true)]
|
||||
// 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
|
||||
if (metadata.ModelType.GetTypeInfo().IsValueType && attributes.Any(attribute => attribute is RequiredAttribute))
|
||||
{
|
||||
if (!DataMemberModelValidatorProvider.IsRequiredDataMember(metadata.ContainerType, attributes))
|
||||
{
|
||||
yield return new ErrorModelValidator(Resources.FormatMissingDataMemberIsRequired(metadata.PropertyName, metadata.ContainerType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public sealed class ModelValidatedEventArgs : EventArgs
|
||||
{
|
||||
public ModelValidatedEventArgs([NotNull] ModelValidationContext validationContext,
|
||||
[NotNull] ModelValidationNode parentNode)
|
||||
{
|
||||
ValidationContext = validationContext;
|
||||
ParentNode = parentNode;
|
||||
}
|
||||
|
||||
public ModelValidationContext ValidationContext { get; private set; }
|
||||
|
||||
public ModelValidationNode ParentNode { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public sealed class ModelValidatingEventArgs : CancelEventArgs
|
||||
{
|
||||
public ModelValidatingEventArgs([NotNull] ModelValidationContext validationContext,
|
||||
[NotNull] ModelValidationNode parentNode)
|
||||
{
|
||||
ValidationContext = validationContext;
|
||||
ParentNode = parentNode;
|
||||
}
|
||||
|
||||
public ModelValidationContext ValidationContext { get; private set; }
|
||||
|
||||
public ModelValidationNode ParentNode { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelValidationContext
|
||||
{
|
||||
public ModelValidationContext([NotNull] ModelMetadata metadata,
|
||||
[NotNull] ModelStateDictionary modelState,
|
||||
[NotNull] IModelMetadataProvider metadataProvider,
|
||||
[NotNull] IEnumerable<IModelValidatorProvider> validatorProviders)
|
||||
{
|
||||
ModelMetadata = metadata;
|
||||
ModelState = modelState;
|
||||
MetadataProvider = metadataProvider;
|
||||
ValidatorProviders = validatorProviders;
|
||||
}
|
||||
|
||||
public ModelValidationContext([NotNull] ModelValidationContext parentContext,
|
||||
[NotNull] ModelMetadata metadata)
|
||||
{
|
||||
ModelMetadata = metadata;
|
||||
ContainerMetadata = parentContext.ModelMetadata;
|
||||
ModelState = parentContext.ModelState;
|
||||
MetadataProvider = parentContext.MetadataProvider;
|
||||
ValidatorProviders = parentContext.ValidatorProviders;
|
||||
}
|
||||
|
||||
public ModelMetadata ModelMetadata { get; private set; }
|
||||
|
||||
public ModelMetadata ContainerMetadata { get; private set; }
|
||||
|
||||
public ModelStateDictionary ModelState { get; private set; }
|
||||
|
||||
public IModelMetadataProvider MetadataProvider { get; private set; }
|
||||
|
||||
public IEnumerable<IModelValidatorProvider> ValidatorProviders { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelValidationNode
|
||||
{
|
||||
private readonly List<ModelValidationNode> _childNodes;
|
||||
|
||||
public ModelValidationNode(ModelMetadata modelMetadata, string modelStateKey)
|
||||
: this(modelMetadata, modelStateKey, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ModelValidationNode([NotNull] ModelMetadata modelMetadata,
|
||||
[NotNull] string modelStateKey,
|
||||
IEnumerable<ModelValidationNode> childNodes)
|
||||
{
|
||||
ModelMetadata = modelMetadata;
|
||||
ModelStateKey = modelStateKey;
|
||||
_childNodes = (childNodes != null) ? childNodes.ToList() : new List<ModelValidationNode>();
|
||||
}
|
||||
|
||||
public event EventHandler<ModelValidatedEventArgs> Validated;
|
||||
|
||||
public event EventHandler<ModelValidatingEventArgs> Validating;
|
||||
|
||||
public ICollection<ModelValidationNode> ChildNodes
|
||||
{
|
||||
get { return _childNodes; }
|
||||
}
|
||||
|
||||
public ModelMetadata ModelMetadata { get; private set; }
|
||||
|
||||
public string ModelStateKey { get; private set; }
|
||||
|
||||
public bool ValidateAllProperties { get; set; }
|
||||
|
||||
public bool SuppressValidation { get; set; }
|
||||
|
||||
public void CombineWith(ModelValidationNode otherNode)
|
||||
{
|
||||
if (otherNode != null && !otherNode.SuppressValidation)
|
||||
{
|
||||
Validated += otherNode.Validated;
|
||||
Validating += otherNode.Validating;
|
||||
var otherChildNodes = otherNode._childNodes;
|
||||
for (var i = 0; i < otherChildNodes.Count; i++)
|
||||
{
|
||||
var childNode = otherChildNodes[i];
|
||||
_childNodes.Add(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidated(ModelValidatedEventArgs e)
|
||||
{
|
||||
if (Validated != null)
|
||||
{
|
||||
Validated(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidating(ModelValidatingEventArgs e)
|
||||
{
|
||||
if (Validating != null)
|
||||
{
|
||||
Validating(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private object TryConvertContainerToMetadataType(ModelValidationNode parentNode)
|
||||
{
|
||||
if (parentNode != null)
|
||||
{
|
||||
var containerInstance = parentNode.ModelMetadata.Model;
|
||||
if (containerInstance != null)
|
||||
{
|
||||
var expectedContainerType = ModelMetadata.ContainerType;
|
||||
if (expectedContainerType != null)
|
||||
{
|
||||
if (expectedContainerType.IsCompatibleWith(containerInstance))
|
||||
{
|
||||
return containerInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Validate(ModelValidationContext validationContext)
|
||||
{
|
||||
Validate(validationContext, parentNode: null);
|
||||
}
|
||||
|
||||
public void Validate(ModelValidationContext validationContext, ModelValidationNode parentNode)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw Error.ArgumentNull("validationContext");
|
||||
}
|
||||
|
||||
if (SuppressValidation)
|
||||
{
|
||||
// no-op
|
||||
return;
|
||||
}
|
||||
|
||||
// pre-validation steps
|
||||
var validatingEventArgs = new ModelValidatingEventArgs(validationContext, parentNode);
|
||||
OnValidating(validatingEventArgs);
|
||||
if (validatingEventArgs.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateChildren(validationContext);
|
||||
ValidateThis(validationContext, parentNode);
|
||||
|
||||
// post-validation steps
|
||||
var validatedEventArgs = new ModelValidatedEventArgs(validationContext, parentNode);
|
||||
OnValidated(validatedEventArgs);
|
||||
}
|
||||
|
||||
private void ValidateChildren(ModelValidationContext validationContext)
|
||||
{
|
||||
for (var i = 0; i < _childNodes.Count; i++)
|
||||
{
|
||||
var child = _childNodes[i];
|
||||
var childValidationContext = new ModelValidationContext(validationContext, child.ModelMetadata);
|
||||
child.Validate(childValidationContext, this);
|
||||
}
|
||||
|
||||
if (ValidateAllProperties)
|
||||
{
|
||||
ValidateProperties(validationContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateProperties(ModelValidationContext validationContext)
|
||||
{
|
||||
var modelState = validationContext.ModelState;
|
||||
|
||||
var model = ModelMetadata.Model;
|
||||
var updatedMetadata = validationContext.MetadataProvider.GetMetadataForType(() => model, ModelMetadata.ModelType);
|
||||
|
||||
foreach (var propertyMetadata in updatedMetadata.Properties)
|
||||
{
|
||||
// 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.
|
||||
var propertyKeyRoot = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName);
|
||||
|
||||
if (modelState.IsValidField(propertyKeyRoot))
|
||||
{
|
||||
var propertyValidators = GetValidators(validationContext, propertyMetadata);
|
||||
var propertyValidationContext = new ModelValidationContext(validationContext, propertyMetadata);
|
||||
foreach (var propertyValidator in propertyValidators)
|
||||
{
|
||||
foreach (var propertyResult in propertyValidator.Validate(propertyValidationContext))
|
||||
{
|
||||
var thisErrorKey = ModelBindingHelper.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName);
|
||||
modelState.AddModelError(thisErrorKey, propertyResult.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateThis(ModelValidationContext validationContext, ModelValidationNode parentNode)
|
||||
{
|
||||
var modelState = validationContext.ModelState;
|
||||
if (!modelState.IsValidField(ModelStateKey))
|
||||
{
|
||||
return; // short-circuit
|
||||
}
|
||||
|
||||
// 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
|
||||
// validation error
|
||||
if (parentNode == null && ModelMetadata.Model == null)
|
||||
{
|
||||
var trueModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, ModelMetadata.GetDisplayName());
|
||||
modelState.AddModelError(trueModelStateKey, Resources.Validation_ValueNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
var container = TryConvertContainerToMetadataType(parentNode);
|
||||
var validators = GetValidators(validationContext, ModelMetadata).ToArray();
|
||||
for (var i = 0; i < validators.Length; i++)
|
||||
{
|
||||
var validator = validators[i];
|
||||
foreach (var validationResult in validator.Validate(validationContext))
|
||||
{
|
||||
var trueModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, validationResult.MemberName);
|
||||
modelState.AddModelError(trueModelStateKey, validationResult.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<IModelValidator> GetValidators(ModelValidationContext validationContext, ModelMetadata metadata)
|
||||
{
|
||||
return validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelValidationResult
|
||||
{
|
||||
public ModelValidationResult(string memberName, string message)
|
||||
{
|
||||
MemberName = memberName ?? string.Empty;
|
||||
Message = message ?? string.Empty;
|
||||
}
|
||||
|
||||
public string MemberName { get; private set; }
|
||||
|
||||
public string Message { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class RequiredMemberModelValidator : IModelValidator
|
||||
{
|
||||
public bool IsRequired
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#if NET45
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ValidatableObjectAdapter : IModelValidator
|
||||
{
|
||||
public bool IsRequired
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
var model = context.ModelMetadata.Model;
|
||||
if (model == null)
|
||||
{
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
|
||||
var validatable = model as IValidatableObject;
|
||||
if (validatable == null)
|
||||
{
|
||||
var message = Resources.FormatValidatableObjectAdapter_IncompatibleType(
|
||||
typeof(IValidatableObject).Name,
|
||||
model.GetType());
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var validationContext = new ValidationContext(validatable, serviceProvider: null, items: null);
|
||||
return ConvertResults(validatable.Validate(validationContext));
|
||||
}
|
||||
|
||||
private IEnumerable<ModelValidationResult> ConvertResults(IEnumerable<ValidationResult> results)
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result != ValidationResult.Success)
|
||||
{
|
||||
if (result.MemberNames == null || !result.MemberNames.Any())
|
||||
{
|
||||
yield return new ModelValidationResult(memberName: null, message: result.ErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var memberName in result.MemberNames)
|
||||
{
|
||||
yield return new ModelValidationResult(memberName, result.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,12 +2,17 @@
|
|||
"version": "0.1-alpha-*",
|
||||
"dependencies": {
|
||||
"Common": "",
|
||||
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
|
||||
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
|
||||
"Newtonsoft.Json": "5.0.8"
|
||||
},
|
||||
"configurations": {
|
||||
"net45": {},
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"System.ComponentModel.DataAnnotations": "",
|
||||
"System.Runtime.Serialization": ""
|
||||
}
|
||||
},
|
||||
"k10": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.0.0.0",
|
||||
|
|
@ -22,12 +27,15 @@
|
|||
"System.Reflection": "4.0.10.0",
|
||||
"System.Reflection.Emit.ILGeneration": "4.0.0.0",
|
||||
"System.Reflection.Emit.Lightweight": "4.0.0.0",
|
||||
"System.Reflection.Compatibility": "4.0.0.0",
|
||||
"System.Reflection.Extensions": "4.0.0.0",
|
||||
"System.Reflection.Primitives": "4.0.0.0",
|
||||
"System.Resources.ResourceManager": "4.0.0.0",
|
||||
"System.Runtime": "4.0.20.0",
|
||||
"System.Runtime.Extensions": "4.0.10.0",
|
||||
"System.Threading.Tasks": "4.0.0.0"
|
||||
"System.Threading.Tasks": "4.0.0.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.0.0.0",
|
||||
"Microsoft.ComponentModel.DataAnnotations": "0.1-alpha-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,10 +61,13 @@ namespace Microsoft.AspNet.Mvc
|
|||
yield return describe.Transient<IModelBinder, MutableObjectModelBinder>();
|
||||
yield return describe.Transient<IModelBinder, ComplexModelDtoModelBinder>();
|
||||
|
||||
yield return describe.Transient<IInputFormatter, JsonInputFormatter>();
|
||||
|
||||
yield return describe.Transient<INestedProviderManager<FilterProviderContext>, NestedProviderManager<FilterProviderContext>>();
|
||||
yield return describe.Transient<INestedProvider<FilterProviderContext>, DefaultFilterProvider>();
|
||||
|
||||
yield return describe.Transient<IInputFormatter, JsonInputFormatter>();
|
||||
yield return describe.Singleton<IModelValidatorProvider, DataAnnotationsModelValidatorProvider>();
|
||||
yield return describe.Singleton<IModelValidatorProvider, DataMemberModelValidatorProvider>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -20,12 +21,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var binder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
List<int> boundCollection = binder.BindComplexCollectionFromIndexes(bindingContext, new[] { "foo", "bar", "baz" });
|
||||
var boundCollection = binder.BindComplexCollectionFromIndexes(bindingContext, new[] { "foo", "bar", "baz" });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42, 0, 200 }, boundCollection.ToArray());
|
||||
// TODO: Validation
|
||||
// Assert.Equal(new[] { "someName[foo]", "someName[baz]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray());
|
||||
Assert.Equal(new[] { "someName[foo]", "someName[baz]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -42,12 +42,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var binder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
List<int> boundCollection = binder.BindComplexCollectionFromIndexes(bindingContext, indexNames: null);
|
||||
var boundCollection = binder.BindComplexCollectionFromIndexes(bindingContext, indexNames: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42, 100 }, boundCollection.ToArray());
|
||||
// TODO: Validation
|
||||
// Assert.Equal(new[] { "someName[0]", "someName[1]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray());
|
||||
Assert.Equal(new[] { "someName[0]", "someName[1]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -97,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var binder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
List<int> boundCollection = binder.BindSimpleCollection(bindingContext: null, rawValue: new object[0], culture: null);
|
||||
var boundCollection = binder.BindSimpleCollection(bindingContext: null, rawValue: new object[0], culture: null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(boundCollection);
|
||||
|
|
@ -111,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var binder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
List<int> boundCollection = binder.BindSimpleCollection(bindingContext: null, rawValue: null, culture: null);
|
||||
var boundCollection = binder.BindSimpleCollection(bindingContext: null, rawValue: null, culture: null);
|
||||
|
||||
// Assert
|
||||
Assert.Null(boundCollection);
|
||||
|
|
@ -124,25 +123,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var culture = CultureInfo.GetCultureInfo("fr-FR");
|
||||
var bindingContext = GetModelBindingContext(new SimpleHttpValueProvider());
|
||||
|
||||
// TODO: Validation
|
||||
// ModelValidationNode childValidationNode = null;
|
||||
ModelValidationNode childValidationNode = null;
|
||||
Mock.Get<IModelBinder>(bindingContext.ModelBinder)
|
||||
.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
{
|
||||
Assert.Equal("someName", mbc.ModelName);
|
||||
// childValidationNode = mbc.ValidationNode;
|
||||
childValidationNode = mbc.ValidationNode;
|
||||
mbc.Model = 42;
|
||||
return true;
|
||||
});
|
||||
var modelBinder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
List<int> boundCollection = modelBinder.BindSimpleCollection(bindingContext, new int[1], culture);
|
||||
var boundCollection = modelBinder.BindSimpleCollection(bindingContext, new int[1], culture);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42 }, boundCollection.ToArray());
|
||||
// Assert.Equal(new[] { childValidationNode }, bindingContext.ValidationNode.ChildNodes.ToArray());
|
||||
Assert.Equal(new[] { childValidationNode }, bindingContext.ValidationNode.ChildNodes.ToArray());
|
||||
}
|
||||
|
||||
private static ModelBindingContext GetModelBindingContext(IValueProvider valueProvider)
|
||||
|
|
|
|||
|
|
@ -4,36 +4,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
public class ComplexModelDtoResultTest
|
||||
{
|
||||
// TODO: Validation
|
||||
////[Fact]
|
||||
////public void Constructor_ThrowsIfValidationNodeIsNull()
|
||||
////{
|
||||
//// // Act & assert
|
||||
//// ExceptionAssert.ThrowsArgumentNull(
|
||||
//// () => new ComplexModelDtoResult("some string"),
|
||||
//// "validationNode");
|
||||
////}
|
||||
[Fact]
|
||||
public void Constructor_ThrowsIfValidationNodeIsNull()
|
||||
{
|
||||
// Act & assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => new ComplexModelDtoResult("some string", validationNode: null),
|
||||
"validationNode");
|
||||
}
|
||||
|
||||
// TODO: Validation
|
||||
//[Fact]
|
||||
//public void Constructor_SetsProperties()
|
||||
//{
|
||||
// // Arrange
|
||||
// ModelValidationNode validationNode = GetValidationNode();
|
||||
[Fact]
|
||||
public void Constructor_SetsProperties()
|
||||
{
|
||||
// Arrange
|
||||
var validationNode = GetValidationNode();
|
||||
|
||||
// // Act
|
||||
// ComplexModelDtoResult result = new ComplexModelDtoResult("some string", validationNode);
|
||||
// Act
|
||||
var result = new ComplexModelDtoResult("some string", validationNode);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("some string", result.Model);
|
||||
// Assert.Equal(validationNode, result.ValidationNode);
|
||||
//}
|
||||
// Assert
|
||||
Assert.Equal("some string", result.Model);
|
||||
Assert.Equal(validationNode, result.ValidationNode);
|
||||
}
|
||||
|
||||
//private static ModelValidationNode GetValidationNode()
|
||||
//{
|
||||
// EmptyModelMetadataProvider provider = new EmptyModelMetadataProvider();
|
||||
// ModelMetadata metadata = provider.GetMetadataForType(null, typeof(object));
|
||||
// return new ModelValidationNode(metadata, "someKey");
|
||||
//}
|
||||
private static ModelValidationNode GetValidationNode()
|
||||
{
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(null, typeof(object));
|
||||
return new ModelValidationNode(metadata, "someKey");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -12,22 +13,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public void BindModel_SuccessfulBind_RunsValidationAndReturnsModel()
|
||||
{
|
||||
// Arrange
|
||||
bool validationCalled = false;
|
||||
var validationCalled = false;
|
||||
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
FallbackToEmptyPrefix = true,
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)),
|
||||
ModelName = "someName",
|
||||
//ModelState = executionContext.Controller.ViewData.ModelState,
|
||||
//PropertyFilter = _ => true,
|
||||
ModelState = new ModelStateDictionary(),
|
||||
ValueProvider = new SimpleValueProvider
|
||||
{
|
||||
{ "someName", "dummyValue" }
|
||||
}
|
||||
},
|
||||
ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
|
||||
};
|
||||
|
||||
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
mockIntBinder
|
||||
.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns(
|
||||
|
|
@ -38,22 +39,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
||||
|
||||
context.Model = 42;
|
||||
// TODO: Validation
|
||||
// mbc.ValidationNode.Validating += delegate { validationCalled = true; };
|
||||
bindingContext.ValidationNode.Validating += delegate { validationCalled = true; };
|
||||
return true;
|
||||
});
|
||||
|
||||
//binderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */);
|
||||
IModelBinder shimBinder = new CompositeModelBinder(mockIntBinder.Object);
|
||||
var shimBinder = new CompositeModelBinder(mockIntBinder.Object);
|
||||
|
||||
// Act
|
||||
bool isBound = shimBinder.BindModel(bindingContext);
|
||||
var isBound = shimBinder.BindModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(isBound);
|
||||
Assert.Equal(42, bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.True(validationCalled);
|
||||
|
||||
Assert.True(validationCalled);
|
||||
Assert.True(bindingContext.ModelState.IsValid);
|
||||
}
|
||||
|
||||
|
|
@ -61,23 +60,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public void BindModel_SuccessfulBind_ComplexTypeFallback_RunsValidationAndReturnsModel()
|
||||
{
|
||||
// Arrange
|
||||
bool validationCalled = false;
|
||||
List<int> expectedModel = new List<int> { 1, 2, 3, 4, 5 };
|
||||
var validationCalled = false;
|
||||
var expectedModel = new List<int> { 1, 2, 3, 4, 5 };
|
||||
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
FallbackToEmptyPrefix = true,
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
|
||||
ModelName = "someName",
|
||||
//ModelState = executionContext.Controller.ViewData.ModelState,
|
||||
//PropertyFilter = _ => true,
|
||||
ModelState = new ModelStateDictionary(),
|
||||
ValueProvider = new SimpleValueProvider
|
||||
{
|
||||
{ "someOtherName", "dummyValue" }
|
||||
}
|
||||
},
|
||||
ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
|
||||
};
|
||||
|
||||
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
mockIntBinder
|
||||
.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns(
|
||||
|
|
@ -93,12 +92,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider);
|
||||
|
||||
mbc.Model = expectedModel;
|
||||
// TODO: Validation
|
||||
// mbc.ValidationNode.Validating += delegate { validationCalled = true; };
|
||||
mbc.ValidationNode.Validating += delegate { validationCalled = true; };
|
||||
return true;
|
||||
});
|
||||
|
||||
//binderProviders.RegisterBinderForType(typeof(List<int>), mockIntBinder.Object, false /* suppressPrefixCheck */);
|
||||
IModelBinder shimBinder = new CompositeModelBinder(mockIntBinder.Object);
|
||||
|
||||
// Act
|
||||
|
|
@ -107,36 +104,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.True(isBound);
|
||||
Assert.Equal(expectedModel, bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.True(validationCalled);
|
||||
// Assert.True(bindingContext.ModelState.IsValid);
|
||||
Assert.True(validationCalled);
|
||||
Assert.True(bindingContext.ModelState.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindModel_UnsuccessfulBind_BinderFails_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IModelBinder> mockListBinder = new Mock<IModelBinder>();
|
||||
var mockListBinder = new Mock<IModelBinder>();
|
||||
mockListBinder.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns(false)
|
||||
.Verifiable();
|
||||
|
||||
IModelBinder shimBinder = (IModelBinder)mockListBinder.Object;
|
||||
var shimBinder = (IModelBinder)mockListBinder.Object;
|
||||
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
FallbackToEmptyPrefix = false,
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
|
||||
};
|
||||
|
||||
// Act
|
||||
bool isBound = shimBinder.BindModel(bindingContext);
|
||||
var isBound = shimBinder.BindModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(isBound);
|
||||
Assert.Null(bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.True(bindingContext.ModelState.IsValid);
|
||||
Assert.True(bindingContext.ModelState.IsValid);
|
||||
mockListBinder.Verify();
|
||||
}
|
||||
|
||||
|
|
@ -145,17 +140,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
// Arrange
|
||||
var innerBinder = Mock.Of<IModelBinder>();
|
||||
CompositeModelBinder shimBinder = new CompositeModelBinder(innerBinder);
|
||||
var shimBinder = new CompositeModelBinder(innerBinder);
|
||||
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
FallbackToEmptyPrefix = true,
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)),
|
||||
//ModelState = executionContext.Controller.ViewData.ModelState
|
||||
ModelState = new ModelStateDictionary()
|
||||
};
|
||||
|
||||
// Act
|
||||
bool isBound = shimBinder.BindModel(bindingContext);
|
||||
var isBound = shimBinder.BindModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(isBound);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
// Arrange
|
||||
var valueProvider = new SimpleHttpValueProvider();
|
||||
ModelBindingContext bindingContext = GetBindingContext(valueProvider, Mock.Of<IModelBinder>());
|
||||
var bindingContext = GetBindingContext(valueProvider, Mock.Of<IModelBinder>());
|
||||
var binder = new KeyValuePairModelBinder<int, string>();
|
||||
|
||||
// Act
|
||||
|
|
@ -20,8 +21,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.False(retVal);
|
||||
Assert.Null(bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.Empty(bindingContext.ValidationNode.ChildNodes);
|
||||
Assert.Empty(bindingContext.ValidationNode.ChildNodes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
// Arrange
|
||||
var valueProvider = new SimpleHttpValueProvider();
|
||||
ModelBindingContext bindingContext = GetBindingContext(valueProvider);
|
||||
var bindingContext = GetBindingContext(valueProvider);
|
||||
var binder = new KeyValuePairModelBinder<int, string>();
|
||||
|
||||
// Act
|
||||
|
|
@ -38,28 +38,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.True(retVal);
|
||||
Assert.Null(bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.Equal(new[] { "someName.key" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray());
|
||||
Assert.Equal(new[] { "someName.key" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindModel_SubBindingSucceeds()
|
||||
{
|
||||
// Arrange
|
||||
IModelBinder innerBinder = new CompositeModelBinder(CreateStringBinder(), CreateIntBinder());
|
||||
var innerBinder = new CompositeModelBinder(CreateStringBinder(), CreateIntBinder());
|
||||
var valueProvider = new SimpleHttpValueProvider();
|
||||
ModelBindingContext bindingContext = GetBindingContext(valueProvider, innerBinder);
|
||||
var bindingContext = GetBindingContext(valueProvider, innerBinder);
|
||||
|
||||
var binder = new KeyValuePairModelBinder<int, string>();
|
||||
|
||||
// Act
|
||||
bool retVal = binder.BindModel(bindingContext);
|
||||
var retVal = binder.BindModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(retVal);
|
||||
Assert.Equal(new KeyValuePair<int, string>(42, "some-value"), bindingContext.Model);
|
||||
// TODO: Validation
|
||||
// Assert.Equal(new[] { "someName.key", "someName.value" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray());
|
||||
Assert.Equal(new[] { "someName.key", "someName.value" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -71,13 +69,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
// Act
|
||||
int model;
|
||||
bool retVal = binder.TryBindStrongModel(bindingContext, "key", out model);
|
||||
var retVal = binder.TryBindStrongModel(bindingContext, "key", out model);
|
||||
|
||||
// Assert
|
||||
Assert.True(retVal);
|
||||
Assert.Equal(42, model);
|
||||
// TODO: Validation
|
||||
// Assert.Single(bindingContext.ValidationNode.ChildNodes);
|
||||
Assert.Single(bindingContext.ValidationNode.ChildNodes);
|
||||
Assert.Empty(bindingContext.ModelState);
|
||||
}
|
||||
|
||||
|
|
@ -100,33 +97,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
// Act
|
||||
int model;
|
||||
bool retVal = binder.TryBindStrongModel(bindingContext, "key", out model);
|
||||
var retVal = binder.TryBindStrongModel(bindingContext, "key", out model);
|
||||
|
||||
// Assert
|
||||
Assert.True(retVal);
|
||||
Assert.Equal(default(int), model);
|
||||
// TODO: Validation
|
||||
// Assert.Single(bindingContext.ValidationNode.ChildNodes);
|
||||
Assert.Single(bindingContext.ValidationNode.ChildNodes);
|
||||
Assert.Empty(bindingContext.ModelState);
|
||||
}
|
||||
|
||||
private static ModelBindingContext GetBindingContext(IValueProvider valueProvider, IModelBinder innerBinder = null)
|
||||
{
|
||||
var metataProvider = new EmptyModelMetadataProvider();
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = metataProvider.GetMetadataForType(null, typeof(KeyValuePair<int, string>)),
|
||||
ModelName = "someName",
|
||||
ValueProvider = valueProvider,
|
||||
ModelBinder = innerBinder ?? CreateIntBinder(),
|
||||
MetadataProvider = metataProvider
|
||||
MetadataProvider = metataProvider,
|
||||
ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
||||
private static IModelBinder CreateIntBinder()
|
||||
{
|
||||
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
mockIntBinder
|
||||
.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
|
|
@ -143,8 +140,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
private static IModelBinder CreateStringBinder()
|
||||
{
|
||||
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
||||
mockIntBinder
|
||||
var mockStringBinder = new Mock<IModelBinder>();
|
||||
mockStringBinder
|
||||
.Setup(o => o.BindModel(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
{
|
||||
|
|
@ -155,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
}
|
||||
return false;
|
||||
});
|
||||
return mockIntBinder.Object;
|
||||
return mockStringBinder.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public void CopyConstructor()
|
||||
{
|
||||
// Arrange
|
||||
ModelBindingContext originalBindingContext = new ModelBindingContext
|
||||
var originalBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)),
|
||||
ModelName = "theName",
|
||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
};
|
||||
|
||||
// Act
|
||||
ModelBindingContext newBindingContext = new ModelBindingContext(originalBindingContext);
|
||||
var newBindingContext = new ModelBindingContext(originalBindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Null(newBindingContext.ModelMetadata);
|
||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public void ModelProperty_ThrowsIfModelMetadataDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
ModelBindingContext bindingContext = new ModelBindingContext();
|
||||
var bindingContext = new ModelBindingContext();
|
||||
|
||||
// Act & assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public void ModelAndModelTypeAreFedFromModelMetadata()
|
||||
{
|
||||
// Act
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int))
|
||||
};
|
||||
|
|
@ -53,38 +53,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal(typeof(int), bindingContext.ModelType);
|
||||
}
|
||||
|
||||
// TODO: Validation
|
||||
//[Fact]
|
||||
//public void ValidationNodeProperty()
|
||||
//{
|
||||
// // Act
|
||||
// ModelBindingContext bindingContext = new ModelBindingContext
|
||||
// {
|
||||
// ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int))
|
||||
// };
|
||||
[Fact]
|
||||
public void ValidationNodeProperty_DefaultValues()
|
||||
{
|
||||
// Act
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int)),
|
||||
ModelName = "theInt"
|
||||
};
|
||||
|
||||
// // Act & assert
|
||||
// MemberHelper.TestPropertyWithDefaultInstance(bindingContext, "ValidationNode", new ModelValidationNode(bindingContext.ModelMetadata, "someName"));
|
||||
//}
|
||||
// Act
|
||||
var validationNode = bindingContext.ValidationNode;
|
||||
|
||||
// TODO: Validation
|
||||
//[Fact]
|
||||
//public void ValidationNodeProperty_DefaultValues()
|
||||
//{
|
||||
// // Act
|
||||
// ModelBindingContext bindingContext = new ModelBindingContext
|
||||
// {
|
||||
// ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int)),
|
||||
// ModelName = "theInt"
|
||||
// };
|
||||
|
||||
// // Act
|
||||
// ModelValidationNode validationNode = bindingContext.ValidationNode;
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(validationNode);
|
||||
// Assert.Equal(bindingContext.ModelMetadata, validationNode.ModelMetadata);
|
||||
// Assert.Equal(bindingContext.ModelName, validationNode.ModelStateKey);
|
||||
//}
|
||||
// Assert
|
||||
Assert.NotNull(validationNode);
|
||||
Assert.Equal(bindingContext.ModelMetadata, validationNode.ModelMetadata);
|
||||
Assert.Equal(bindingContext.ModelName, validationNode.ModelStateKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
|
|||
{
|
||||
// GetMetadataForProperties
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForPropertiesNullContainerTypeThrows()
|
||||
{
|
||||
// Arrange
|
||||
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
//[Fact]
|
||||
//public void GetMetadataForPropertiesNullContainerTypeThrows()
|
||||
//{
|
||||
// // Arrange
|
||||
// TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => provider.GetMetadataForProperties(new Object(), containerType: null),
|
||||
"containerType");
|
||||
}
|
||||
// // Act & Assert
|
||||
// ExceptionAssert.ThrowsArgumentNull(
|
||||
// () => provider.GetMetadataForProperties(new Object(), containerType: null),
|
||||
// "containerType");
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForPropertiesCreatesMetadataForAllPropertiesOnModelWithPropertyValues()
|
||||
|
|
@ -72,17 +72,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
|
|||
|
||||
// GetMetadataForProperty
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForPropertyNullContainerTypeThrows()
|
||||
{
|
||||
// Arrange
|
||||
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
//[Fact]
|
||||
//public void GetMetadataForPropertyNullContainerTypeThrows()
|
||||
//{
|
||||
// // Arrange
|
||||
// TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: null, propertyName: "propertyName"),
|
||||
"containerType");
|
||||
}
|
||||
// // Act & Assert
|
||||
// ExceptionAssert.ThrowsArgumentNull(
|
||||
// () => provider.GetMetadataForProperty(modelAccessor: null, containerType: null, propertyName: "propertyName"),
|
||||
// "containerType");
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows()
|
||||
|
|
@ -167,17 +167,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test.Binders
|
|||
|
||||
// GetMetadataForType
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForTypeNullModelTypeThrows()
|
||||
{
|
||||
// Arrange
|
||||
TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
//[Fact]
|
||||
//public void GetMetadataForTypeNullModelTypeThrows()
|
||||
//{
|
||||
// // Arrange
|
||||
// TestableAssociatedMetadataProvider provider = new TestableAssociatedMetadataProvider();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => provider.GetMetadataForType(() => new Object(), modelType: null),
|
||||
"modelType");
|
||||
}
|
||||
// // Act & Assert
|
||||
// ExceptionAssert.ThrowsArgumentNull(
|
||||
// () => provider.GetMetadataForType(() => new Object(), modelType: null),
|
||||
// "modelType");
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForTypeIncludesAttributesOnType()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class AssociatedValidatorProviderTest
|
||||
{
|
||||
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsThrowsIfMetadataIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForType(null, typeof(object));
|
||||
var provider = new Mock<AssociatedValidatorProvider> { CallBase = true };
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => provider.Object.GetValidators(metadata: null),
|
||||
"metadata");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsForPropertyWithLocalAttributes()
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<Attribute> callbackAttributes = null;
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes");
|
||||
var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
|
||||
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<Attribute>>()))
|
||||
.Callback<ModelMetadata, IEnumerable<Attribute>>((m, attributes) => callbackAttributes = attributes)
|
||||
.Returns(() => null)
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
provider.Object.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
provider.Verify();
|
||||
Assert.True(callbackAttributes.Any(a => a is RequiredAttribute));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsForPropertyWithMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<Attribute> callbackAttributes = null;
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes");
|
||||
Mock<TestableAssociatedValidatorProvider> provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
|
||||
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<Attribute>>()))
|
||||
.Callback<ModelMetadata, IEnumerable<Attribute>>((m, attributes) => callbackAttributes = attributes)
|
||||
.Returns(() => null)
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
provider.Object.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
provider.Verify();
|
||||
Assert.True(callbackAttributes.Any(a => a is RangeAttribute));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsForPropertyWithMixedAttributes()
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<Attribute> callbackAttributes = null;
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes");
|
||||
Mock<TestableAssociatedValidatorProvider> provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
|
||||
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<Attribute>>()))
|
||||
.Callback<ModelMetadata, IEnumerable<Attribute>>((m, attributes) => callbackAttributes = attributes)
|
||||
.Returns(() => null)
|
||||
.Verifiable();
|
||||
|
||||
// Act
|
||||
provider.Object.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
provider.Verify();
|
||||
Assert.True(callbackAttributes.Any(a => a is RangeAttribute));
|
||||
Assert.True(callbackAttributes.Any(a => a is RequiredAttribute));
|
||||
}
|
||||
|
||||
private class PropertyModel
|
||||
{
|
||||
[Required]
|
||||
public int LocalAttributes { get; set; }
|
||||
|
||||
[Range(10, 100)]
|
||||
public string MetadataAttributes { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(10, 100)]
|
||||
public double MixedAttributes { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TestableAssociatedValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes)
|
||||
{
|
||||
return AbstractGetValidators(metadata, attributes);
|
||||
}
|
||||
|
||||
public abstract IEnumerable<IModelValidator> AbstractGetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataAnnotationsModelValidatorProviderTest
|
||||
{
|
||||
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void UnknownValidationAttributeGetsDefaultAdapter()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(DummyClassWithDummyValidationAttribute));
|
||||
|
||||
// Act
|
||||
IEnumerable<IModelValidator> validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
var validator = validators.Single();
|
||||
Assert.IsType<DataAnnotationsModelValidator>(validator);
|
||||
}
|
||||
|
||||
private class DummyValidationAttribute : ValidationAttribute
|
||||
{
|
||||
}
|
||||
|
||||
[DummyValidation]
|
||||
private class DummyClassWithDummyValidationAttribute
|
||||
{
|
||||
}
|
||||
|
||||
// Default IValidatableObject adapter factory
|
||||
|
||||
[Fact]
|
||||
public void IValidatableObjectGetsAValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider();
|
||||
var mockValidatable = new Mock<IValidatableObject>();
|
||||
var metadata = _metadataProvider.GetMetadataForType(() => null, mockValidatable.Object.GetType());
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
Assert.Single(validators);
|
||||
}
|
||||
|
||||
// Integration with metadata system
|
||||
|
||||
[Fact]
|
||||
public void DoesNotReadPropertyValue()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider();
|
||||
var model = new ObservableModel();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => model.TheProperty, typeof(ObservableModel), "TheProperty");
|
||||
var context = new ModelValidationContext(metadata, null, null, null);
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata).ToArray();
|
||||
var results = validators.SelectMany(o => o.Validate(context)).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(validators);
|
||||
Assert.False(model.PropertyWasRead());
|
||||
}
|
||||
|
||||
private class ObservableModel
|
||||
{
|
||||
private bool _propertyWasRead;
|
||||
|
||||
public string TheProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
_propertyWasRead = true;
|
||||
return "Hello";
|
||||
}
|
||||
}
|
||||
|
||||
public bool PropertyWasRead()
|
||||
{
|
||||
return _propertyWasRead;
|
||||
}
|
||||
}
|
||||
|
||||
private class BaseModel
|
||||
{
|
||||
public virtual string MyProperty { get; set; }
|
||||
}
|
||||
|
||||
private class DerivedModel : BaseModel
|
||||
{
|
||||
[StringLength(10)]
|
||||
public override string MyProperty
|
||||
{
|
||||
get { return base.MyProperty; }
|
||||
set { base.MyProperty = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using Xunit;
|
||||
using Xunit.Extensions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataAnnotationsModelValidatorTest
|
||||
{
|
||||
private static DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void ConstructorGuards()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForType(null, typeof(object));
|
||||
var attribute = new RequiredAttribute();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => new DataAnnotationsModelValidator(null),
|
||||
"attribute");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValuesSet()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
var attribute = new RequiredAttribute();
|
||||
|
||||
// Act
|
||||
var validator = new DataAnnotationsModelValidator(attribute);
|
||||
|
||||
// Assert
|
||||
Assert.Same(attribute, validator.Attribute);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ValidateSetsMemberNamePropertyDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
_metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length"),
|
||||
"Length"
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
_metadataProvider.GetMetadataForType(() => new object(), typeof(SampleModel)),
|
||||
"SampleModel"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[PropertyData("ValidateSetsMemberNamePropertyDataSet")]
|
||||
public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(ModelMetadata metadata, string expectedMemberName)
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Protected()
|
||||
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
|
||||
.Callback((object o, ValidationContext context) =>
|
||||
{
|
||||
Assert.Equal(expectedMemberName, context.MemberName);
|
||||
})
|
||||
.Returns(ValidationResult.Success)
|
||||
.Verifiable();
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
attribute.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateWithIsValidTrue()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Setup(a => a.IsValid(metadata.Model)).Returns(true);
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateWithIsValidFalse()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Setup(a => a.IsValid(metadata.Model)).Returns(false);
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
var validationResult = result.Single();
|
||||
Assert.Equal("", validationResult.MemberName);
|
||||
Assert.Equal(attribute.Object.FormatErrorMessage("Length"), validationResult.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidatateWithValidationResultSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Protected()
|
||||
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
|
||||
.Returns(ValidationResult.Success);
|
||||
DataAnnotationsModelValidator validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateReturnsSingleValidationResultIfMemberNameSequenceIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
const string errorMessage = "Some error message";
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Protected()
|
||||
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
|
||||
.Returns(new ValidationResult(errorMessage, memberNames: null));
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
ModelValidationResult validationResult = Assert.Single(results);
|
||||
Assert.Equal(errorMessage, validationResult.Message);
|
||||
Assert.Empty(validationResult.MemberName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateReturnsSingleValidationResultIfOneMemberNameIsSpecified()
|
||||
{
|
||||
// Arrange
|
||||
const string errorMessage = "A different error message";
|
||||
var metadata = _metadataProvider.GetMetadataForType(() => new object(), typeof(object));
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Protected()
|
||||
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
|
||||
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
ModelValidationResult validationResult = Assert.Single(results);
|
||||
Assert.Equal(errorMessage, validationResult.Message);
|
||||
Assert.Equal("FirstName", validationResult.MemberName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateReturnsMemberNameIfItIsDifferentFromDisplayName()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForType(() => new SampleModel(), typeof(SampleModel));
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Protected()
|
||||
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
|
||||
.Returns(new ValidationResult("Name error", new[] { "Name" }));
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object);
|
||||
var validationContext = CreateValidationContext(metadata);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
ModelValidationResult validationResult = Assert.Single(results);
|
||||
Assert.Equal("Name", validationResult.MemberName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRequiredTests()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
|
||||
|
||||
// Act & Assert
|
||||
Assert.False(new DataAnnotationsModelValidator(new RangeAttribute(10, 20)).IsRequired);
|
||||
Assert.True(new DataAnnotationsModelValidator(new RequiredAttribute()).IsRequired);
|
||||
Assert.True(new DataAnnotationsModelValidator(new DerivedRequiredAttribute()).IsRequired);
|
||||
}
|
||||
|
||||
private static ModelValidationContext CreateValidationContext(ModelMetadata metadata)
|
||||
{
|
||||
return new ModelValidationContext(metadata, null, null, null);
|
||||
}
|
||||
|
||||
class DerivedRequiredAttribute : RequiredAttribute
|
||||
{
|
||||
}
|
||||
|
||||
class SampleModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
using System.Runtime.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataMemberModelValidatorProviderTest
|
||||
{
|
||||
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void ClassWithoutAttributes_NoValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataMemberModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithoutAttributes), "TheProperty");
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(validators);
|
||||
}
|
||||
|
||||
class ClassWithoutAttributes
|
||||
{
|
||||
public int TheProperty { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassWithDataMemberIsRequiredTrue_Validator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataMemberModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredTrue), "TheProperty");
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
var validator = Assert.Single(validators);
|
||||
Assert.True(validator.IsRequired);
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
class ClassWithDataMemberIsRequiredTrue
|
||||
{
|
||||
[DataMember(IsRequired = true)]
|
||||
public int TheProperty { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassWithDataMemberIsRequiredFalse_NoValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataMemberModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredFalse), "TheProperty");
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(validators);
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
class ClassWithDataMemberIsRequiredFalse
|
||||
{
|
||||
[DataMember(IsRequired = false)]
|
||||
public int TheProperty { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassWithDataMemberIsRequiredTrueWithoutDataContract_NoValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataMemberModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract), "TheProperty");
|
||||
|
||||
// Act
|
||||
var validators = provider.GetValidators(metadata);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(validators);
|
||||
}
|
||||
|
||||
class ClassWithDataMemberIsRequiredTrueWithoutDataContract
|
||||
{
|
||||
[DataMember(IsRequired = true)]
|
||||
public int TheProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ErrorModelValidatorTest
|
||||
{
|
||||
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void ConstructorGuards()
|
||||
{
|
||||
// Act and Assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => new ErrorModelValidator(errorMessage: null),
|
||||
"errorMessage");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var validator = new ErrorModelValidator("error");
|
||||
|
||||
// Act and Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => validator.Validate(null), "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class InvalidModelValidatorProviderTest
|
||||
{
|
||||
private static DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsReturnsNothingForValidModel()
|
||||
{
|
||||
// Arrange
|
||||
var validatorProvider = new InvalidModelValidatorProvider();
|
||||
|
||||
// Act
|
||||
var validators = validatorProvider.GetValidators(_metadataProvider.GetMetadataForType(null, typeof(ValidModel)));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(validators);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsReturnsInvalidModelValidatorsForInvalidModelType()
|
||||
{
|
||||
// Arrange
|
||||
var name = typeof(InvalidModel).FullName;
|
||||
var validatorProvider = new InvalidModelValidatorProvider();
|
||||
|
||||
// Act
|
||||
var validators = validatorProvider.GetValidators(_metadataProvider.GetMetadataForType(null, typeof(InvalidModel)));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, validators.Count());
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => validators.ElementAt(0).Validate(null),
|
||||
"Non-public property 'Internal' on type '" + name + "' is attributed with one or more validation attributes. Validation attributes on non-public properties are not supported. Consider using a public property for validation instead.");
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => validators.ElementAt(1).Validate(null),
|
||||
"Field 'Field' on type '" + name + "' is attributed with one or more validation attributes. Validation attributes on fields are not supported. Consider using a public property for validation instead.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidatorsReturnsInvalidModelValidatorsForInvalidModelProperty()
|
||||
{
|
||||
// Arrange
|
||||
var name = typeof(InvalidModel).FullName;
|
||||
var validatorProvider = new InvalidModelValidatorProvider();
|
||||
|
||||
// Act
|
||||
var validators = validatorProvider.GetValidators(_metadataProvider.GetMetadataForProperty(null, typeof(InvalidModel), "Value"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, validators.Count());
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => validators.First().Validate(null),
|
||||
"Property 'Value' on type '" + name + "' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].");
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class ValidModel
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
[StringLength(10)]
|
||||
public string Ref { get; set; }
|
||||
|
||||
[DataMember]
|
||||
internal string Internal { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember(IsRequired = true)]
|
||||
public int Value { get; set; }
|
||||
|
||||
public string Field;
|
||||
}
|
||||
|
||||
public class InvalidModel
|
||||
{
|
||||
[Required]
|
||||
public string Ref { get; set; }
|
||||
|
||||
[StringLength(10)]
|
||||
[RegularExpression("pattern")]
|
||||
internal string Internal { get; set; }
|
||||
|
||||
[Required]
|
||||
public int Value { get; set; }
|
||||
|
||||
[StringLength(10)]
|
||||
public string Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Test;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelValidationNodeTest
|
||||
{
|
||||
[Fact]
|
||||
public void ConstructorSetsCollectionInstance()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = GetModelMetadata();
|
||||
var modelStateKey = "someKey";
|
||||
var childNodes = new[]
|
||||
{
|
||||
new ModelValidationNode(metadata, "someKey0"),
|
||||
new ModelValidationNode(metadata, "someKey1")
|
||||
};
|
||||
|
||||
// Act
|
||||
var node = new ModelValidationNode(metadata, modelStateKey, childNodes);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(childNodes, node.ChildNodes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PropertiesAreSet()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = GetModelMetadata();
|
||||
var modelStateKey = "someKey";
|
||||
|
||||
// Act
|
||||
var node = new ModelValidationNode(metadata, modelStateKey);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(metadata, node.ModelMetadata);
|
||||
Assert.Equal(modelStateKey, node.ModelStateKey);
|
||||
Assert.NotNull(node.ChildNodes);
|
||||
Assert.Empty(node.ChildNodes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CombineWith()
|
||||
{
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
|
||||
var allChildNodes = new[]
|
||||
{
|
||||
new ModelValidationNode(GetModelMetadata(), "key1"),
|
||||
new ModelValidationNode(GetModelMetadata(), "key2"),
|
||||
new ModelValidationNode(GetModelMetadata(), "key3"),
|
||||
};
|
||||
|
||||
var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
|
||||
parentNode1.ChildNodes.Add(allChildNodes[0]);
|
||||
parentNode1.Validating += (sender, e) => log.Add("Validating parent1.");
|
||||
parentNode1.Validated += (sender, e) => log.Add("Validated parent1.");
|
||||
|
||||
var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
|
||||
parentNode2.ChildNodes.Add(allChildNodes[1]);
|
||||
parentNode2.ChildNodes.Add(allChildNodes[2]);
|
||||
parentNode2.Validating += (sender, e) => log.Add("Validating parent2.");
|
||||
parentNode2.Validated += (sender, e) => log.Add("Validated parent2.");
|
||||
var context = CreateContext();
|
||||
|
||||
// Act
|
||||
parentNode1.CombineWith(parentNode2);
|
||||
parentNode1.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }, log.ToArray());
|
||||
Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CombineWith_OtherNodeIsSuppressed_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
|
||||
var allChildNodes = new[]
|
||||
{
|
||||
new ModelValidationNode(GetModelMetadata(), "key1"),
|
||||
new ModelValidationNode(GetModelMetadata(), "key2"),
|
||||
new ModelValidationNode(GetModelMetadata(), "key3"),
|
||||
};
|
||||
|
||||
var expectedChildNodes = new[]
|
||||
{
|
||||
allChildNodes[0]
|
||||
};
|
||||
|
||||
var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1");
|
||||
parentNode1.ChildNodes.Add(allChildNodes[0]);
|
||||
parentNode1.Validating += (sender, e) => log.Add("Validating parent1.");
|
||||
parentNode1.Validated += (sender, e) => log.Add("Validated parent1.");
|
||||
|
||||
var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2");
|
||||
parentNode2.ChildNodes.Add(allChildNodes[1]);
|
||||
parentNode2.ChildNodes.Add(allChildNodes[2]);
|
||||
parentNode2.Validating += (sender, e) => log.Add("Validating parent2.");
|
||||
parentNode2.Validated += (sender, e) => log.Add("Validated parent2.");
|
||||
parentNode2.SuppressValidation = true;
|
||||
var context = CreateContext();
|
||||
|
||||
// Act
|
||||
parentNode1.CombineWith(parentNode2);
|
||||
parentNode1.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Validating parent1.", "Validated parent1." }, log.ToArray());
|
||||
Assert.Equal(expectedChildNodes, parentNode1.ChildNodes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Ordering()
|
||||
{
|
||||
// Proper order of invocation:
|
||||
// 1. OnValidating()
|
||||
// 2. Child validators
|
||||
// 3. This validator
|
||||
// 4. OnValidated()
|
||||
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
var model = new LoggingValidatableObject(log);
|
||||
var modelMetadata = GetModelMetadata(model);
|
||||
var childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty");
|
||||
var node = new ModelValidationNode(modelMetadata, "theKey");
|
||||
node.Validating += (sender, e) => log.Add("In OnValidating()");
|
||||
node.Validated += (sender, e) => log.Add("In OnValidated()");
|
||||
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.ValidStringProperty"));
|
||||
var context = CreateContext(modelMetadata);
|
||||
|
||||
// Act
|
||||
node.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "In OnValidating()", "In LoggingValidatonAttribute.IsValid()", "In IValidatableObject.Validate()", "In OnValidated()" }, log.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_SkipsRemainingValidationIfModelStateIsInvalid()
|
||||
{
|
||||
// Because a property validator fails, the model validator shouldn't run
|
||||
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
var model = new LoggingValidatableObject(log);
|
||||
var modelMetadata = GetModelMetadata(model);
|
||||
var childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "InvalidStringProperty");
|
||||
var node = new ModelValidationNode(modelMetadata, "theKey");
|
||||
node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty"));
|
||||
node.Validating += (sender, e) => log.Add("In OnValidating()");
|
||||
node.Validated += (sender, e) => log.Add("In OnValidated()");
|
||||
var context = CreateContext(modelMetadata);
|
||||
|
||||
// Act
|
||||
node.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "In OnValidating()", "In IValidatableObject.Validate()", "In OnValidated()" }, log.ToArray());
|
||||
Assert.Equal("Sample error message", context.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_SkipsValidationIfHandlerCancels()
|
||||
{
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
var model = new LoggingValidatableObject(log);
|
||||
var modelMetadata = GetModelMetadata(model);
|
||||
var node = new ModelValidationNode(modelMetadata, "theKey");
|
||||
node.Validating += (sender, e) =>
|
||||
{
|
||||
log.Add("In OnValidating()");
|
||||
e.Cancel = true;
|
||||
};
|
||||
node.Validated += (sender, e) => log.Add("In OnValidated()");
|
||||
var context = CreateContext(modelMetadata);
|
||||
|
||||
// Act
|
||||
node.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "In OnValidating()" }, log.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_SkipsValidationIfSuppressed()
|
||||
{
|
||||
// Arrange
|
||||
var log = new List<string>();
|
||||
var model = new LoggingValidatableObject(log);
|
||||
var modelMetadata = GetModelMetadata(model);
|
||||
var node = new ModelValidationNode(modelMetadata, "theKey")
|
||||
{
|
||||
SuppressValidation = true
|
||||
};
|
||||
|
||||
node.Validating += (sender, e) => log.Add("In OnValidating()");
|
||||
node.Validated += (sender, e) => log.Add("In OnValidated()");
|
||||
var context = CreateContext();
|
||||
|
||||
// Act
|
||||
node.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(log);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_ThrowsIfControllerContextIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var node = new ModelValidationNode(GetModelMetadata(), "someKey");
|
||||
|
||||
// Act & assert
|
||||
ExceptionAssert.ThrowsArgumentNull(
|
||||
() => node.Validate(null),
|
||||
"validationContext");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void Validate_ValidateAllProperties_AddsValidationErrors()
|
||||
{
|
||||
// Arrange
|
||||
var model = new ValidateAllPropertiesModel
|
||||
{
|
||||
RequiredString = null /* error */,
|
||||
RangedInt = 0 /* error */,
|
||||
ValidString = "dog"
|
||||
};
|
||||
|
||||
var modelMetadata = GetModelMetadata(model);
|
||||
var node = new ModelValidationNode(modelMetadata, "theKey")
|
||||
{
|
||||
ValidateAllProperties = true
|
||||
};
|
||||
var context = CreateContext(modelMetadata);
|
||||
context.ModelState.AddModelError("theKey.RequiredString.Dummy", "existing Error Text");
|
||||
|
||||
// Act
|
||||
node.Validate(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.ModelState.ContainsKey("theKey.RequiredString"));
|
||||
Assert.Equal("existing Error Text", 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"));
|
||||
}
|
||||
|
||||
private static ModelMetadata GetModelMetadata()
|
||||
{
|
||||
return new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object));
|
||||
}
|
||||
|
||||
private static ModelMetadata GetModelMetadata(object o)
|
||||
{
|
||||
return new DataAnnotationsModelMetadataProvider().GetMetadataForType(() => o, o.GetType());
|
||||
}
|
||||
|
||||
private static ModelValidationContext CreateContext(ModelMetadata metadata = null)
|
||||
{
|
||||
var providers = new IModelValidatorProvider[] {
|
||||
new DataAnnotationsModelValidatorProvider(),
|
||||
new DataMemberModelValidatorProvider()
|
||||
};
|
||||
|
||||
return new ModelValidationContext(metadata,
|
||||
new ModelStateDictionary(),
|
||||
new EmptyModelMetadataProvider(),
|
||||
providers);
|
||||
}
|
||||
|
||||
private sealed class LoggingValidatableObject : IValidatableObject
|
||||
{
|
||||
private readonly IList<string> _log;
|
||||
|
||||
public LoggingValidatableObject(IList<string> log)
|
||||
{
|
||||
_log = log;
|
||||
}
|
||||
|
||||
[LoggingValidation]
|
||||
public string ValidStringProperty { get; set; }
|
||||
public string InvalidStringProperty { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
_log.Add("In IValidatableObject.Validate()");
|
||||
yield return new ValidationResult("Sample error message", new[] { "InvalidStringProperty" });
|
||||
}
|
||||
|
||||
private sealed class LoggingValidationAttribute : ValidationAttribute
|
||||
{
|
||||
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
|
||||
{
|
||||
LoggingValidatableObject lvo = (LoggingValidatableObject)value;
|
||||
lvo._log.Add("In LoggingValidatonAttribute.IsValid()");
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ValidateAllPropertiesModel
|
||||
{
|
||||
[Required]
|
||||
public string RequiredString { get; set; }
|
||||
|
||||
[Range(10, 30)]
|
||||
public int RangedInt { get; set; }
|
||||
|
||||
[RegularExpression("dog")]
|
||||
public string ValidString { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,16 @@
|
|||
"Xunit.extensions": "1.9.1"
|
||||
},
|
||||
"configurations": {
|
||||
"net45": { }
|
||||
"net45": {
|
||||
dependencies: {
|
||||
"System.ComponentModel.DataAnnotations": "",
|
||||
"System.Runtime.Serialization": ""
|
||||
}
|
||||
},
|
||||
"k10" : {
|
||||
dependencies: {
|
||||
"Microsoft.ComponentModel.DataAnnotations" : "0.1-alpha-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@ using System.Globalization;
|
|||
using System.Threading;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class CultureReplacer : IDisposable
|
||||
internal class CultureReplacer : IDisposable
|
||||
{
|
||||
private const string _defaultCultureName = "en-GB";
|
||||
private const string _defaultUICultureName = "en-US";
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
using System.Reflection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public static class ExceptionAssert
|
||||
internal static class ExceptionAssert
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that an exception of the given type (or optionally a derived type) is thrown.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces the current culture and UI culture for the test.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class ReplaceCultureAttribute : Xunit.BeforeAfterTestAttribute
|
||||
{
|
||||
private const string _defaultCultureName = "en-GB";
|
||||
private const string _defaultUICultureName = "en-US";
|
||||
private static readonly CultureInfo _defaultCulture = CultureInfo.GetCultureInfo(_defaultCultureName);
|
||||
private CultureInfo _originalCulture;
|
||||
private CultureInfo _originalUICulture;
|
||||
|
||||
public ReplaceCultureAttribute()
|
||||
{
|
||||
Culture = _defaultCulture;
|
||||
UICulture = _defaultCulture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="Thread.CurrentCulture"/> for the test. Defaults to en-GB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We
|
||||
/// want to be able to find bugs where we're accidentally relying on the Invariant instead of the
|
||||
/// user's culture.
|
||||
/// </remarks>
|
||||
public CultureInfo Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="Thread.CurrentUICulture"/> for the test. Defaults to en-US.
|
||||
/// </summary>
|
||||
public CultureInfo UICulture { get; set; }
|
||||
|
||||
public override void Before(MethodInfo methodUnderTest)
|
||||
{
|
||||
_originalCulture = Thread.CurrentThread.CurrentCulture;
|
||||
_originalUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
|
||||
Thread.CurrentThread.CurrentCulture = Culture;
|
||||
Thread.CurrentThread.CurrentUICulture = UICulture;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodUnderTest)
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = _originalCulture;
|
||||
Thread.CurrentThread.CurrentUICulture = _originalUICulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue