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);
                }
            }
        }
Пример #9
0
        /// <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!
        }
Пример #16
0
        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);
        }
Пример #27
0
        /// <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);
        }