public void SetProperty_PropertyIsPreinitialized_DefaultValueAttributeDoesNothing() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithInitializedValueAndDefault)]; // This value won't be used because IsModelBound = false. var result = new ModelBindingResult(model: "bad-value", isModelSet: false, key: "foo"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert var person = Assert.IsType<Person>(bindingContext.Model); Assert.Equal("preinitialized", person.PropertyWithInitializedValueAndDefault); Assert.True(bindingContext.ModelState.IsValid); }
public void ProcessResults_ValueTypeProperty_TriesToSetNullModel_CapturesException() { // Arrange var model = new Person(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // The [DefaultValue] on ValueTypeRequiredWithDefaultValue is ignored by model binding. var expectedValue = 0; // Make ValueTypeRequired invalid. var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequired)); // Make ValueTypeRequiredWithDefaultValue invalid propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert Assert.False(modelStateDictionary.IsValid); // Check ValueTypeRequired error. var modelStateEntry = Assert.Single( modelStateDictionary, entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequired)); Assert.Equal("theModel." + nameof(Person.ValueTypeRequired), modelStateEntry.Key); var modelState = modelStateEntry.Value; Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState); var error = Assert.Single(modelState.Errors); Assert.Equal(string.Empty, error.ErrorMessage); Assert.IsType<NullReferenceException>(error.Exception); // Check ValueTypeRequiredWithDefaultValue error. modelStateEntry = Assert.Single( modelStateDictionary, entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); Assert.Equal("theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue), modelStateEntry.Key); modelState = modelStateEntry.Value; Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState); error = Assert.Single(modelState.Errors); Assert.Equal(string.Empty, error.ErrorMessage); Assert.IsType<NullReferenceException>(error.Exception); Assert.Equal(0, model.ValueTypeRequired); Assert.Equal(expectedValue, model.ValueTypeRequiredWithDefaultValue); }
public void SetProperty_PropertyIsSettable_CallsSetter() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"]; var dtoResult = new ModelBindingResult( new DateTime(2001, 1, 1), key: "foo", isModelSet: true); var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider; var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata); validatorProvider.GetValidators(validatorProviderContext); var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.True(bindingContext.ModelState.IsValid); Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth); }
public void SetProperty_PropertySetterThrows_CapturesException() { // Arrange var model = new ModelWhosePropertySetterThrows(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); bindingContext.ModelName = "foo"; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)]; var result = new ModelBindingResult(model: null, isModelSet: true, key: "foo.NameNoAttribute"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.False(bindingContext.ModelState.IsValid); Assert.Equal(1, bindingContext.ModelState["foo.NameNoAttribute"].Errors.Count); Assert.Equal("This is a different exception." + Environment.NewLine + "Parameter name: value", bindingContext.ModelState["foo.NameNoAttribute"].Errors[0].Exception.Message); }
public void ProcessResults_DataMemberIsRequiredFieldMissing_RaisesModelError() { // Arrange var model = new ModelWithDataMemberIsRequired { Name = "original value", Age = -20 }; var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = new ModelBindingContext { Model = model, ModelMetadata = containerMetadata, ModelName = "theModel", OperationBindingContext = new OperationBindingContext { MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(), ValidatorProvider = Mock.Of<IModelValidatorProvider>() } }; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var nameProperty = containerMetadata.Properties[nameof(model.Name)]; results[nameProperty] = new ModelBindingResult("John Doe", isModelSet: true, key: string.Empty); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert var modelStateDictionary = bindingContext.ModelState; Assert.False(modelStateDictionary.IsValid); Assert.Single(modelStateDictionary); // Check Age error. ModelState modelState; Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState)); var modelError = Assert.Single(modelState.Errors); Assert.Null(modelError.Exception); Assert.NotNull(modelError.ErrorMessage); Assert.Equal("A value for the 'Age' property was not provided.", modelError.ErrorMessage); }
public void SetProperty_CollectionProperty_UpdatesModel( string propertyName, Func<object, object> propertyAccessor, object collection) { // Arrange var model = new CollectionContainer(); var type = model.GetType(); var bindingContext = CreateContext(GetMetadataForType(type), model); var modelState = bindingContext.ModelState; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(type, model); var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName]; var result = new ModelBindingResult(model: collection, isModelSet: true, key: propertyName); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.Equal(collection, propertyAccessor(model)); Assert.True(modelState.IsValid); Assert.Empty(modelState); }
public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError() { // Arrange var model = new Person { DateOfBirth = new DateTime(1900, 1, 1) }; var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)]; var result = new ModelBindingResult(new DateTime(1800, 1, 1), isModelSet: true, key: "foo"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine + "Parameter name: value", bindingContext.ModelState["foo"].Errors[0].Exception.Message); }
protected virtual void SetProperty( ModelBindingContext bindingContext, ModelExplorer modelExplorer, ModelMetadata propertyMetadata, ModelBindingResult dtoResult, IModelValidator requiredValidator) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase; var property = bindingContext.ModelType.GetProperty( propertyMetadata.PropertyName, bindingFlags); if (property == null || !property.CanWrite) { // nothing to do return; } object value; var hasDefaultValue = false; if (dtoResult.IsModelSet) { value = dtoResult.Model; } else { hasDefaultValue = TryGetPropertyDefaultValue(property, out 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. if (value == null) { var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { if (requiredValidator != null) { var propertyExplorer = modelExplorer.GetExplorerForExpression(propertyMetadata, model: null); var validationContext = new ModelValidationContext(bindingContext, propertyExplorer); foreach (var validationResult in requiredValidator.Validate(validationContext)) { bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message); } } } } if (!dtoResult.IsModelSet && !hasDefaultValue) { // If we don't have a value, don't set it on the model and trounce a pre-initialized // value. return; } if (value != null || property.PropertyType.AllowsNullValue()) { try { property.SetValue(bindingContext.Model, value); } catch (Exception ex) { // don't display a duplicate error message if a binding error has already occurred for this field var targetInvocationException = ex as TargetInvocationException; if (targetInvocationException != null && targetInvocationException.InnerException != null) { ex = targetInvocationException.InnerException; } var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { bindingContext.ModelState.AddModelError(modelStateKey, ex); } } } else { // trying to set a non-nullable value type to null, need to make sure there's a message var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { var errorMessage = Resources.ModelBinderConfig_ValueRequired; bindingContext.ModelState.TryAddModelError(modelStateKey, errorMessage); } } }
/// <inheritdoc /> public virtual async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); var model = bindingContext.Model; if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { // If we failed to find data for a top-level model, then generate a // default 'empty' model (or use existing Model) and return it. if (bindingContext.IsTopLevelObject) { if (model == null) { model = CreateEmptyCollection(bindingContext.ModelType); } return(ModelBindingResult.Success(bindingContext.ModelName, model)); } return(ModelBindingResult.NoResult); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); CollectionResult result; if (valueProviderResult == ValueProviderResult.None) { result = await BindComplexCollection(bindingContext); } else { result = await BindSimpleCollection(bindingContext, valueProviderResult); } var boundCollection = result.Model; if (model == null) { model = ConvertToCollectionType(bindingContext.ModelType, boundCollection); } else { // Special case for TryUpdateModelAsync(collection, ...) scenarios. Model is null in all other cases. CopyToModel(model, boundCollection); } Debug.Assert(model != null); if (result.ValidationStrategy != null) { bindingContext.ValidationState.Add(model, new ValidationStateEntry() { Strategy = result.ValidationStrategy, }); } if (valueProviderResult != ValueProviderResult.None) { // If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName. // If we did complex binding, there will already be an entry for each index. bindingContext.ModelState.SetModelValue( bindingContext.ModelName, valueProviderResult); } return(ModelBindingResult.Success(bindingContext.ModelName, model)); }
public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString() { // Arrange var model = new ModelWhosePropertySetterThrows(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); bindingContext.ModelName = "foo"; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["Name"]; var dtoResult = new ModelBindingResult( model: null, isModelSet: true, key: "foo.Name"); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.False(bindingContext.ModelState.IsValid); var error = Assert.Single(bindingContext.ModelState["foo.Name"].Errors); Assert.Equal("This message comes from the [Required] attribute.", error.ErrorMessage); }
public new void SetProperty( ModelBindingContext bindingContext, ModelExplorer modelExplorer, ModelMetadata propertyMetadata, ModelBindingResult dtoResult, IModelValidator requiredValidator) { base.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); }
public void SetProperty_SettingNullableTypeToNull_RequiredValidatorNotPresent_PropertySetterThrows_AddsRequiredMessageString() { // Arrange var model = new ModelWhosePropertySetterThrows(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); bindingContext.ModelName = "foo"; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["NameNoAttribute"]; var dtoResult = new ModelBindingResult( model: null, isModelSet: true, key: "foo.NameNoAttribute"); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.False(bindingContext.ModelState.IsValid); Assert.Equal(1, bindingContext.ModelState["foo.NameNoAttribute"].Errors.Count); Assert.Equal("This is a different exception." + Environment.NewLine + "Parameter name: value", bindingContext.ModelState["foo.NameNoAttribute"].Errors[0].Exception.Message); }
public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); bindingContext.ModelName = " foo"; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["ValueTypeRequired"]; var dtoResult = new ModelBindingResult( model: null, isModelSet: true, key: "foo.ValueTypeRequired"); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.False(bindingContext.ModelState.IsValid); Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage); }
public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"]; var dtoResult = new ModelBindingResult( model: null, isModelSet: true, key: "foo"); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.False(bindingContext.ModelState.IsValid); }
public void SetProperty_PropertyIsReadOnly_DoesNothing() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NonUpdateableProperty)]; var result = new ModelBindingResult(model: null, isModelSet: false, key: "foo"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert // If didn't throw, success! }
public Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { // This method is optimized to use cached tasks when possible and avoid allocating // using Task.FromResult. If you need to make changes of this nature, profile // allocations afterwards and look for Task<ModelBindingResult>. if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted return(ModelBindingResult.NoResultAsync); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { // no entry return(ModelBindingResult.NoResultAsync); } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { var model = valueProviderResult.ConvertTo(bindingContext.ModelType); if (bindingContext.ModelType == typeof(string)) { var modelAsString = model as string; if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && string.IsNullOrWhiteSpace(modelAsString)) { model = null; } } // When converting newModel a null value may indicate a failed conversion for an otherwise required // model (can't set a ValueType to null). This detects if a null model value is acceptable given the // current bindingContext. If not, an error is logged. if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor( valueProviderResult.ToString())); return(ModelBindingResult.FailedAsync(bindingContext.ModelName)); } else { return(ModelBindingResult.SuccessAsync(bindingContext.ModelName, model)); } } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders. return(ModelBindingResult.FailedAsync(bindingContext.ModelName)); } }
public void SetProperty_ValueProvidedAndCanUpdatePropertyTrue_DoesNothing( string propertyName, Func<object, object> propertAccessor) { // Arrange var model = new MyModelTestingCanUpdateProperty(); var type = model.GetType(); var bindingContext = CreateContext(GetMetadataForType(type), model); var modelState = bindingContext.ModelState; var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(type, model); var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName]; var result = new ModelBindingResult( model: new Simple { Name = "Hanna" }, isModelSet: true, key: propertyName); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.Equal("Joe", propertAccessor(model)); Assert.True(modelState.IsValid); Assert.Empty(modelState); }
public void ProcessResults_ValueTypeProperty_NoValue_NoError() { // Arrange var model = new Person(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelState = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // Make ValueTypeRequired invalid. var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(Person.ValueTypeRequired)); // Make ValueTypeRequiredWithDefaultValue invalid propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert Assert.True(modelState.IsValid); }
public void SetProperty_PropertyIsSettable_CallsSetter() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)]; var result = new ModelBindingResult(new DateTime(2001, 1, 1), key: "foo", isModelSet: true); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.True(bindingContext.ModelState.IsValid); Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth); }
public void ProcessResults_ProvideRequiredFields_Success() { // Arrange var model = new Person(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // Make ValueTypeRequired valid. var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)]; results[propertyMetadata] = new ModelBindingResult( model: 41, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequired)); // Make ValueTypeRequiredWithDefaultValue valid. propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: 57, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); // Also remind ProcessResults about PropertyWithDefaultValue -- as BindPropertiesAsync() would. propertyMetadata = containerMetadata.Properties[nameof(Person.PropertyWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(Person.PropertyWithDefaultValue)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert Assert.True(modelStateDictionary.IsValid); Assert.Empty(modelStateDictionary); // Model gets provided values. Assert.Equal(41, model.ValueTypeRequired); Assert.Equal(57, model.ValueTypeRequiredWithDefaultValue); Assert.Equal(0m, model.PropertyWithDefaultValue); // [DefaultValue] has no effect }
public void SetProperty_SettingNonNullableValueTypeToNull_CapturesException() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)]; var result = new ModelBindingResult(model: null, isModelSet: true, key: "foo.DateOfBirth"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert Assert.False(bindingContext.ModelState.IsValid); var entry = Assert.Single(bindingContext.ModelState, kvp => kvp.Key == "foo.DateOfBirth").Value; var error = Assert.Single(entry.Errors); Assert.Equal(string.Empty, error.ErrorMessage); Assert.IsType<NullReferenceException>(error.Exception); }
public void ProcessResults_ReferenceTypePropertyWithBindRequired_RequiredValidatorIgnored() { // Arrange var model = new ModelWithBindRequiredAndRequiredAttribute(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // Make ValueTypeProperty have a value. var propertyMetadata = containerMetadata .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)]; results[propertyMetadata] = new ModelBindingResult( model: 17, isModelSet: true, key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)); // Make ReferenceTypeProperty not have a value. propertyMetadata = containerMetadata .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert Assert.False(modelStateDictionary.IsValid); var entry = Assert.Single( modelStateDictionary, kvp => kvp.Key == "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)) .Value; var error = Assert.Single(entry.Errors); Assert.Null(error.Exception); Assert.Equal("A value for the 'ReferenceTypeProperty' property was not provided.", error.ErrorMessage); // Model gets provided values. Assert.Equal(17, model.ValueTypeProperty); Assert.Null(model.ReferenceTypeProperty); }
public new void SetProperty( ModelBindingContext bindingContext, ModelExplorer modelExplorer, ModelMetadata propertyMetadata, ModelBindingResult result) { base.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); }
public void ProcessResults_Success() { // Arrange var dob = new DateTime(2001, 1, 1); var model = new PersonWithBindExclusion { DateOfBirth = dob }; var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var firstNameProperty = containerMetadata.Properties[nameof(model.FirstName)]; results[firstNameProperty] = new ModelBindingResult( model: "John", isModelSet: true, key: nameof(model.FirstName)); var lastNameProperty = containerMetadata.Properties[nameof(model.LastName)]; results[lastNameProperty] = new ModelBindingResult( model: "Doe", isModelSet: true, key: nameof(model.LastName)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert Assert.Equal("John", model.FirstName); Assert.Equal("Doe", model.LastName); Assert.Equal(dob, model.DateOfBirth); Assert.True(bindingContext.ModelState.IsValid); // Ensure that we add child nodes for all the nodes which have a result (irrespective of if they // are bound or not). Assert.Equal(5, modelValidationNode.ChildNodes.Count); Assert.Collection(modelValidationNode.ChildNodes, child => { Assert.Equal(nameof(model.DateOfBirth), child.Key); Assert.Equal(null, child.Model); }, child => { Assert.Equal(nameof(model.DateOfDeath), child.Key); Assert.Equal(null, child.Model); }, child => { Assert.Equal(nameof(model.FirstName), child.Key); Assert.Equal("John", child.Model); }, child => { Assert.Equal(nameof(model.LastName), child.Key); Assert.Equal("Doe", child.Model); }, child => { Assert.Equal(nameof(model.NonUpdateableProperty), child.Key); Assert.Equal(null, child.Model); }); }
public void ProcessResults_ValueTypePropertyWithBindRequired_SetToNull_CapturesException() { // Arrange var model = new ModelWithBindRequired { Name = "original value", Age = -20 }; var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = new ModelBindingContext() { Model = model, ModelMetadata = containerMetadata, ModelName = "theModel", ModelState = new ModelStateDictionary(), OperationBindingContext = new OperationBindingContext { MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(), ValidatorProvider = Mock.Of<IModelValidatorProvider>() } }; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var propertyMetadata = containerMetadata.Properties[nameof(model.Name)]; results[propertyMetadata] = new ModelBindingResult("John Doe", isModelSet: true, key: "theModel.Name"); // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this // case because the binding exists. propertyMetadata = containerMetadata.Properties[nameof(model.Age)]; results[propertyMetadata] = new ModelBindingResult(model: null, isModelSet: true, key: "theModel.Age"); var testableBinder = new TestableMutableObjectModelBinder(); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert var modelStateDictionary = bindingContext.ModelState; Assert.False(modelStateDictionary.IsValid); Assert.Equal(1, modelStateDictionary.Count); // Check Age error. ModelState modelState; Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState)); Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState); var modelError = Assert.Single(modelState.Errors); Assert.Equal(string.Empty, modelError.ErrorMessage); Assert.IsType<NullReferenceException>(modelError.Exception); }
public void SetProperty_PropertyHasDefaultValue_DefaultValueAttributeDoesNothing() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithDefaultValue)]; var result = new ModelBindingResult(model: null, isModelSet: false, key: "foo"); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result); // Assert var person = Assert.IsType<Person>(bindingContext.Model); Assert.Equal(0m, person.PropertyWithDefaultValue); Assert.True(bindingContext.ModelState.IsValid); }
/// <summary> /// Attempts to bind the model using formatters. /// </summary> /// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param> /// <returns> /// A <see cref="Task{ModelBindingResult}"/> which when completed returns a <see cref="ModelBindingResult"/>. /// </returns> private async Task <ModelBindingResult> BindModelCoreAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // For compatibility with MVC 5.0 for top level object we want to consider an empty key instead of // the parameter name/a custom name. In all other cases (like when binding body to a property) we // consider the entire ModelName as a prefix. var modelBindingKey = bindingContext.IsTopLevelObject ? string.Empty : bindingContext.ModelName; var httpContext = bindingContext.OperationBindingContext.HttpContext; var formatterContext = new InputFormatterContext( httpContext, modelBindingKey, bindingContext.ModelState, bindingContext.ModelMetadata, _readerFactory); var formatters = bindingContext.OperationBindingContext.InputFormatters; var formatter = formatters.FirstOrDefault(f => f.CanRead(formatterContext)); if (formatter == null) { var unsupportedContentType = Resources.FormatUnsupportedContentType( bindingContext.OperationBindingContext.HttpContext.Request.ContentType); bindingContext.ModelState.AddModelError(modelBindingKey, unsupportedContentType); // This model binder is the only handler for the Body binding source and it cannot run twice. Always // tell the model binding system to skip other model binders and never to fall back i.e. indicate a // fatal error. return(ModelBindingResult.Failed(modelBindingKey)); } try { var previousCount = bindingContext.ModelState.ErrorCount; var result = await formatter.ReadAsync(formatterContext); var model = result.Model; // Ensure a "modelBindingKey" entry exists whether or not formatting was successful. bindingContext.ModelState.SetModelValue(modelBindingKey, rawValue: model, attemptedValue: null); if (result.HasError) { // Formatter encountered an error. Do not use the model it returned. As above, tell the model // binding system to skip other model binders and never to fall back. return(ModelBindingResult.Failed(modelBindingKey)); } return(ModelBindingResult.Success(modelBindingKey, model)); } catch (Exception ex) { bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata); // This model binder is the only handler for the Body binding source and it cannot run twice. Always // tell the model binding system to skip other model binders and never to fall back i.e. indicate a // fatal error. return(ModelBindingResult.Failed(modelBindingKey)); } }
public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model); var propertyMetadata = bindingContext.ModelMetadata.Properties["PropertyWithDefaultValue"]; var dtoResult = new ModelBindingResult( model: null, isModelSet: false, key: "foo"); var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider; var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata); validatorProvider.GetValidators(validatorProviderContext); var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetProperty( bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator); // Assert var person = Assert.IsType<Person>(bindingContext.Model); Assert.Equal(123.456m, person.PropertyWithDefaultValue); Assert.True(bindingContext.ModelState.IsValid); }