/// <inheritdoc /> public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // Try to get the XElement. ModelBindingResult result; var xElementMetadata = bindingContext.ModelMetadata.GetMetadataForType(typeof(XElement)); using (var innerContext = bindingContext.EnterNestedScope( xElementMetadata, bindingContext.FieldName, bindingContext.ModelName, bindingContext.Model)) { await _bodyModelBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } // If we got the XElement, create the SalesforceNotifications instance and return that. if (result.IsModelSet) { result = ModelBindingResult.Success(new SalesforceNotifications((XElement)result.Model)); } bindingContext.Result = result; }
internal async Task <ModelBindingResult> TryBindStrongModel <TModel>( ModelBindingContext bindingContext, IModelBinder binder, string propertyName) { var propertyModelMetadata = bindingContext.ModelMetadata.Properties[propertyName]; var propertyModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, propertyName); using (bindingContext.EnterNestedScope( modelMetadata: propertyModelMetadata, fieldName: propertyName, modelName: propertyModelName, model: null)) { await binder.BindModelAsync(bindingContext); var result = bindingContext.Result; if (result != null && result.Value.IsModelSet) { return(result.Value); } else { return(ModelBindingResult.Failed(propertyModelName)); } } }
/// <summary> /// Binds the model. /// </summary> public async Task BindModelAsync(ModelBindingContext bindingContext) { var typeFieldNamePrefix = !string.IsNullOrEmpty(bindingContext.ModelName) ? $"{bindingContext.ModelName}." : ""; var typeNameResult = bindingContext.ValueProvider .GetValue($"{typeFieldNamePrefix}{_baseType.Name}Type"); if (typeNameResult == null || typeNameResult.Count() != 1) { return; } var typeName = typeNameResult.First(); var derivedModelBinderKvp = _derivedModelBinders.First(kvp => kvp.Key.Name == typeName); var derivedModelType = derivedModelBinderKvp.Key; var derivedModelBinder = derivedModelBinderKvp.Value; ModelBindingResult result; using (bindingContext.EnterNestedScope( _modelMetadataProvider.GetMetadataForType(derivedModelType), bindingContext.FieldName, bindingContext.ModelName, model: null )) { await derivedModelBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } bindingContext.Result = result; }
private async ValueTask <ModelBindingResult> BindParameterAsync( ModelBindingContext bindingContext, ModelMetadata parameter, IModelBinder parameterBinder, string fieldName, string modelName) { Debug.Assert(parameter.MetadataKind == ModelMetadataKind.Parameter); ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: parameter, fieldName: fieldName, modelName: modelName, model: null)) { await parameterBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } if (!result.IsModelSet && parameter.IsBindingRequired) { var message = parameter.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } return(result); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var fieldName = nameof(Model.Value); var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); ModelBindingResult valueResult; using (bindingContext.EnterNestedScope( bindingContext.ModelMetadata.Properties[fieldName], fieldName, modelName, model: null)) { await _binder.BindModelAsync(bindingContext); valueResult = bindingContext.Result; } if (!valueResult.IsModelSet) { return; } var model = new Model { FieldName = bindingContext.FieldName, Value = (string)valueResult.Model, }; bindingContext.Result = ModelBindingResult.Success(model); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { //绑定 对象部分 if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // Retrieve the form part containing the JSON var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName); if (valueResult == ValueProviderResult.None) { // The JSON was not found var message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName); bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message); return; } var rawValue = valueResult.FirstValue; // Deserialize the JSON var model = JsonConvert.DeserializeObject(rawValue, bindingContext.ModelType, _jsonOptions.Value.SerializerSettings); // Now, bind each of the IFormFile properties from the other form parts foreach (var property in bindingContext.ModelMetadata.Properties) { //if (property.ModelType != typeof(IFormFile)) if (property.ModelType != typeof(IFormFile)) { continue; } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = fieldName; var propertyModel = bindingContext.ValueProvider.GetValue(fieldName); //var propertyModel = property.PropertyGetter(bindingContext.Model); ModelBindingResult propertyResult; using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel)) { await _formFileModelBinder.BindModelAsync(bindingContext); propertyResult = bindingContext.Result; } if (propertyResult.IsModelSet) { // The IFormFile was sucessfully bound, assign it to the corresponding property of the model property.PropertySetter(model, propertyResult.Model); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } // Set the successfully constructed model as the result of the model binding bindingContext.Result = ModelBindingResult.Success(model); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var typeValue = bindingContext.ValueProvider.GetValue(nameof(ComplexModel.Type)).Values; var nameValue = bindingContext.ValueProvider.GetValue(nameof(ComplexModel.Name)).Values; var finalModel = new ComplexModel { Name = nameValue, Type = typeValue }; var innerType = LookupType(typeValue); if (innerType != null) { finalModel.Parameters = Activator.CreateInstance(innerType); var modelMetadata = _modelMetadataProvider.GetMetadataForType(innerType); var modelBinder = _modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { Metadata = modelMetadata, CacheToken = modelMetadata }); var modelName = bindingContext.BinderModelName == null ? "Parameters" : $"{bindingContext.BinderModelName}.Parameters"; using (var scope = bindingContext.EnterNestedScope(modelMetadata, modelName, modelName, finalModel.Parameters)) { await modelBinder.BindModelAsync(bindingContext); } } bindingContext.Result = ModelBindingResult.Success(finalModel); return; }
public async Task BindModelAsync(ModelBindingContext bindingContext) { var messageTypeModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "messageType"); var messageTypeResult = bindingContext.ValueProvider.GetValue(messageTypeModelName); if (messageTypeResult == ValueProviderResult.None) { bindingContext.Result = ModelBindingResult.Failed(); return; } IModelBinder binder; if (!_binders.TryGetValue(messageTypeResult.FirstValue, out binder)) { bindingContext.Result = ModelBindingResult.Failed(); return; } // Now know the type exists in the assembly. var type = Type.GetType(messageTypeResult.FirstValue); var metadata = _metadataProvider.GetMetadataForType(type); ModelBindingResult result; using (bindingContext.EnterNestedScope(metadata, bindingContext.FieldName, bindingContext.ModelName, model: null)) { await binder.BindModelAsync(bindingContext); result = bindingContext.Result; } bindingContext.Result = result; }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (!int.TryParse(bindingContext.ValueProvider.GetValue("id").FirstValue, out int productId)) { throw new Exception("The product id was not provided"); } var editModel = new ProductConfigurationEditModel { Fields = productRepository.GetProductFields(productId) }; for (int i = 0; i < editModel.Fields.Count; i++) { BaseField field = editModel.Fields[i]; ModelMetadata modelMetadata = modelMetadataProvider.GetMetadataForType(field.GetType()); IModelBinder modelBinder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { Metadata = modelMetadata, CacheToken = modelMetadata }); string modelName = $"{bindingContext.BinderModelName}.Fields[{i}]".TrimStart('.'); using (var scope = bindingContext.EnterNestedScope(modelMetadata, modelName, modelName, field)) { await modelBinder.BindModelAsync(bindingContext); } } bindingContext.Result = ModelBindingResult.Success(editModel); }
private async Task BindModelCoreAsync(ModelBindingContext bindingContext) { // Create model first (if necessary) to avoid reporting errors about properties when activation fails. if (bindingContext.Model == null) { bindingContext.Model = CreateModel(bindingContext); } for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++) { var property = bindingContext.ModelMetadata.Properties[i]; if (!CanBindProperty(bindingContext, property)) { continue; } // Pass complex (including collection) values down so that binding system does not unnecessarily // recreate instances or overwrite inner properties that are not bound. No need for this with simple // values because they will be overwritten if binding succeeds. Arrays are never reused because they // cannot be resized. object propertyModel = null; if (property.PropertyGetter != null && property.IsComplexType && !property.ModelType.IsArray) { propertyModel = property.PropertyGetter(bindingContext.Model); } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: property, fieldName: fieldName, modelName: modelName, model: propertyModel)) { await BindProperty(bindingContext); result = bindingContext.Result; } if (result.IsModelSet) { SetProperty(bindingContext, modelName, property, result); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); _logger.DoneAttemptingToBindModel(bindingContext); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(paramName: nameof(bindingContext)); } //recupere a parte do formulario que contem o JSon var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName); if (valueResult == ValueProviderResult.None) { // O JSon não foi encontrado var message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName); bindingContext.ModelState.TryAddModelError(key: bindingContext.ModelName, message); return; } var rawValue = valueResult.FirstValue; // desserializar o modelo JSON var model = JsonConvert.DeserializeObject(rawValue, bindingContext.ModelType, _jsonOptions.Value.SerializerSettings); //Agora, vincule cada uma das propriedades IFormFile das outras partes do formulário foreach (var property in bindingContext.ModelMetadata.Properties) { if (property.ModelType != typeof(IFormFile)) { continue; } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = fieldName; var propertyModel = property.PropertyGetter(bindingContext.Model); ModelBindingResult propertyResult; using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel)) { await _formFileModelBinder.BindModelAsync(bindingContext); propertyResult = bindingContext.Result; } if (propertyResult.IsModelSet) { // O IFormFile foi vinculado com sucesso, atribua - o à propriedade correspondente da propriedade do modelo PropertySetter property.PropertySetter(model, propertyResult.Model); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(key: modelName, message); } } //Defina o modelo construído com sucesso como resultado do modelo binding bindingContext.Result = ModelBindingResult.Success(model); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } _logger.AttemptingToBindModel(bindingContext); // Property name can be null if the model metadata represents a type (rather than a property or parameter). var headerName = bindingContext.FieldName; // Do not set ModelBindingResult to Failed on not finding the value in the header as we want the inner // ModelBinder to do that. This would give a chance to the inner binder to add more useful information. // For example, SimpleTypeModelBinder adds a model error when binding to let's say an integer and the // model is null. var request = bindingContext.HttpContext.Request; if (!request.Headers.ContainsKey(headerName)) { _logger.FoundNoValueInRequest(bindingContext); } if (InnerModelBinder == null) { BindWithoutInnerBinder(bindingContext); return; } var headerValueProvider = GetHeaderValueProvider(headerName, bindingContext); // Capture the top level object here as entering nested scope would make it 'false'. var isTopLevelObject = bindingContext.IsTopLevelObject; // Create a new binding scope in order to supply the HeaderValueProvider so that the binders like // SimpleTypeModelBinder can find values from header. ModelBindingResult result; using (bindingContext.EnterNestedScope( bindingContext.ModelMetadata, fieldName: bindingContext.FieldName, modelName: bindingContext.ModelName, model: bindingContext.Model)) { bindingContext.IsTopLevelObject = isTopLevelObject; bindingContext.ValueProvider = headerValueProvider; await InnerModelBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } bindingContext.Result = result; _logger.DoneAttemptingToBindModel(bindingContext); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName); if (valueResult == ValueProviderResult.None) { string message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName); bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message); return; } string rawValue = valueResult.FirstValue; object model = JsonSerializer.Deserialize(rawValue, bindingContext.ModelType); foreach (var property in bindingContext.ModelMetadata.Properties) { if (property.ModelType != typeof(IFormFile)) { continue; } string fieldName = property.BinderModelName ?? property.PropertyName; string modelName = fieldName; object propertyModel = property.PropertyGetter(bindingContext.Model); ModelBindingResult propertyResult; using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel)) { await _formFileModelBinder.BindModelAsync(bindingContext); propertyResult = bindingContext.Result; } if (propertyResult.IsModelSet) { property.PropertySetter(model, propertyResult.Model); } else if (property.IsBindingRequired) { string message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } bindingContext.Result = ModelBindingResult.Success(model); }
private bool CanValueBindAnyModelProperties(ModelBindingContext bindingContext) { if (bindingContext.ModelMetadata.Properties.Count == 0) { return(false); } var hasBindableProperty = false; var isAnyPropertyEnabledForValueProviderBasedBinding = false; for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++) { var propertyMetadata = bindingContext.ModelMetadata.Properties[i]; if (!CanBindProperty(bindingContext, propertyMetadata)) { continue; } hasBindableProperty = true; var bindingSource = propertyMetadata.BindingSource; if (bindingSource == null || !bindingSource.IsGreedy) { isAnyPropertyEnabledForValueProviderBasedBinding = true; var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName; var modelName = ModelNames.CreatePropertyModelName( bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope( modelMetadata: propertyMetadata, fieldName: fieldName, modelName: modelName, model: null)) { if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return(true); } } } } if (hasBindableProperty && !isAnyPropertyEnabledForValueProviderBasedBinding) { return(true); } return(false); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName); if (valueResult == ValueProviderResult.None) { var message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName); bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message); return; } var rawValue = valueResult.FirstValue; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var model = JsonSerializer.Deserialize(rawValue, bindingContext.ModelType, options); foreach (ModelMetadata property in bindingContext.ModelMetadata.Properties) { if (property.ModelType != typeof(IFormFile) && property.ModelType != typeof(IFormFile[]) && property.ModelType != typeof(List <IFormFile>)) { continue; } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = fieldName; var propertyModel = property.PropertyGetter(bindingContext.Model); ModelBindingResult propertyResult; using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel)) { await formFileModelBinder.BindModelAsync(bindingContext).ConfigureAwait(false); propertyResult = bindingContext.Result; } if (propertyResult.IsModelSet) { property.PropertySetter(model, propertyResult.Model); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } bindingContext.Result = ModelBindingResult.Success(model); }
private async ValueTask <ModelBindingResult> BindPropertyAsync( ModelBindingContext bindingContext, ModelMetadata property, IModelBinder propertyBinder, string fieldName, string modelName) { Debug.Assert(property.MetadataKind == ModelMetadataKind.Property); // Pass complex (including collection) values down so that binding system does not unnecessarily // recreate instances or overwrite inner properties that are not bound. No need for this with simple // values because they will be overwritten if binding succeeds. Arrays are never reused because they // cannot be resized. object propertyModel = null; if (property.PropertyGetter != null && property.IsComplexType && !property.ModelType.IsArray) { propertyModel = property.PropertyGetter(bindingContext.Model); } ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: property, fieldName: fieldName, modelName: modelName, model: propertyModel)) { await propertyBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } if (result.IsModelSet) { SetProperty(bindingContext, modelName, property, result); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } return(result); }
/// <summary> /// Binds the current model from the context. /// </summary> /// <param name="bc">The binding context</param> /// <returns>An asynchronous task</returns> public async Task BindModelAsync(ModelBindingContext bc) { var result = ModelBindingResult.Failed(); var typeName = ""; // Get the requested abstract type if (bc.ModelType == typeof(PageEditRegionBase)) { typeName = bc.ValueProvider.GetValue(bc.ModelName + ".CLRType").FirstValue; } else if (bc.ModelType == typeof(Extend.IField)) { typeName = bc.ValueProvider.GetValue(bc.ModelName.Replace(".Value", "") + ".CLRType").FirstValue; } else if (bc.ModelType == typeof(Extend.Block)) { typeName = bc.ValueProvider.GetValue(bc.ModelName.Replace(".Value", "") + ".CLRType").FirstValue; } if (!String.IsNullOrEmpty(typeName)) { try { if (binders.ContainsKey(typeName)) { // Get the binder for the abstract type var item = binders[typeName]; var metadata = provider.GetMetadataForType(item.Type); // Let the default binders take care of it once // that the real type has been discovered. ModelBindingResult scoped; using (bc.EnterNestedScope( metadata, bc.FieldName, bc.ModelName, model: null)) { await item.Binder.BindModelAsync(bc); scoped = bc.Result; } result = scoped; } } catch { } } bc.Result = result; }
internal async Task <ModelBindingResult> TryBindStrongModel <TModel>( ModelBindingContext bindingContext, IModelBinder binder, string propertyName, string propertyModelName) { var propertyModelMetadata = bindingContext.ModelMetadata.Properties[propertyName]; using (bindingContext.EnterNestedScope( modelMetadata: propertyModelMetadata, fieldName: propertyName, modelName: propertyModelName, model: null)) { await binder.BindModelAsync(bindingContext); return(bindingContext.Result); } }
// Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // is [ "1", "2" ] and needs to be converted to an int[]. // Internal for testing. internal async Task <CollectionResult> BindSimpleCollection( ModelBindingContext bindingContext, ValueProviderResult values) { var boundCollection = new List <TElement>(); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement)); foreach (var value in values) { bindingContext.ValueProvider = new CompositeValueProvider { // our temporary provider goes at the front of the list new ElementalValueProvider(bindingContext.ModelName, value, values.Culture), bindingContext.ValueProvider }; object boundValue = null; using (bindingContext.EnterNestedScope( elementMetadata, fieldName: bindingContext.FieldName, modelName: bindingContext.ModelName, model: null)) { await ElementBinder.BindModelAsync(bindingContext); if (bindingContext.Result != null && bindingContext.Result.Value.IsModelSet) { boundValue = bindingContext.Result.Value.Model; boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue)); } } } return(new CollectionResult { Model = boundCollection }); }
// Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // is [ "1", "2" ] and needs to be converted to an int[]. // Internal for testing. internal async Task <CollectionResult> BindSimpleCollection( ModelBindingContext bindingContext, ValueProviderResult values) { var boundCollection = new List <TElement>(); var elementMetadata = bindingContext.ModelMetadata.ElementMetadata; foreach (var value in values) { bindingContext.ValueProvider = new CompositeValueProvider { // our temporary provider goes at the front of the list new ElementalValueProvider(bindingContext.ModelName, value, values.Culture), bindingContext.ValueProvider }; // Enter new scope to change ModelMetadata and isolate element binding operations. using (bindingContext.EnterNestedScope( elementMetadata, fieldName: bindingContext.FieldName, modelName: bindingContext.ModelName, model: null)) { await ElementBinder.BindModelAsync(bindingContext); if (bindingContext.Result.IsModelSet) { var boundValue = bindingContext.Result.Model; boundCollection.Add(CastOrDefault <TElement>(boundValue)); } } } return(new CollectionResult { Model = boundCollection }); }
private bool CanBindAnyModelProperties(ModelBindingContext bindingContext) { if (bindingContext.ModelMetadata.Properties.Count == 0) { return(false); } for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++) { var propertyMetadata = bindingContext.ModelMetadata.Properties[i]; if (!CanBindProperty(bindingContext, propertyMetadata)) { continue; } var bindingSource = propertyMetadata.BindingSource; if (bindingSource != null && bindingSource.IsGreedy) { return(true); } var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope(propertyMetadata, fieldName, modelName, null)) { if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return(true); } } } return(false); }
private bool CanValueBindAnyModelProperties(ModelBindingContext bindingContext) { // If there are no properties on the model, there is nothing to bind. We are here means this is not a top // level object. So we return false. if (bindingContext.ModelMetadata.Properties.Count == 0) { return(false); } // We want to check to see if any of the properties of the model can be bound using the value providers, // because that's all that MutableObjectModelBinder can handle. // // However, because a property might specify a custom binding source ([FromForm]), it's not correct // for us to just try bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName), // because that may include other value providers - that would lead us to mistakenly create the model // when the data is coming from a source we should use (ex: value found in query string, but the // model has [FromForm]). // // To do this we need to enumerate the properties, and see which of them provide a binding source // through metadata, then we decide what to do. // // If a property has a binding source, and it's a greedy source, then it's not // allowed to come from a value provider, so we skip it. // // If a property has a binding source, and it's a non-greedy source, then we'll filter the // the value providers to just that source, and see if we can find a matching prefix // (see CanBindValue). // // If a property does not have a binding source, then it's fair game for any value provider. // // If any property meets the above conditions and has a value from valueproviders, then we'll // create the model and try to bind it. OR if ALL properties of the model have a greedy source, // then we go ahead and create it. // var hasBindableProperty = false; var isAnyPropertyEnabledForValueProviderBasedBinding = false; foreach (var propertyMetadata in bindingContext.ModelMetadata.Properties) { if (!CanBindProperty(bindingContext, propertyMetadata)) { continue; } hasBindableProperty = true; // This check will skip properties which are marked explicitly using a non value binder. var bindingSource = propertyMetadata.BindingSource; if (bindingSource == null || !bindingSource.IsGreedy) { isAnyPropertyEnabledForValueProviderBasedBinding = true; var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName; var modelName = ModelNames.CreatePropertyModelName( bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope( modelMetadata: propertyMetadata, fieldName: fieldName, modelName: modelName, model: null)) { // If any property can return a true value. if (CanBindValue(bindingContext)) { return(true); } } } } if (hasBindableProperty && !isAnyPropertyEnabledForValueProviderBasedBinding) { // All the properties are marked with a non value provider based marker like [FromHeader] or // [FromBody]. return(true); } return(false); }
private int CanBindAnyModelItem(ModelBindingContext bindingContext) { // If there are no properties on the model, and no constructor parameters, there is nothing to bind. We are here means this is not a top // level object. So we return false. var modelMetadata = bindingContext.ModelMetadata; var performsConstructorBinding = bindingContext.Model == null && modelMetadata.BoundConstructor != null; if (modelMetadata.Properties.Count == 0 && (!performsConstructorBinding || modelMetadata.BoundConstructor.BoundConstructorParameters.Count == 0)) { Log.NoPublicSettableItems(_logger, bindingContext); return(NoDataAvailable); } // We want to check to see if any of the properties of the model can be bound using the value providers or // a greedy binder. // // Because a property might specify a custom binding source ([FromForm]), it's not correct // for us to just try bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName); // that may include other value providers - that would lead us to mistakenly create the model // when the data is coming from a source we should use (ex: value found in query string, but the // model has [FromForm]). // // To do this we need to enumerate the properties, and see which of them provide a binding source // through metadata, then we decide what to do. // // If a property has a binding source, and it's a greedy source, then it's always bound. // // If a property has a binding source, and it's a non-greedy source, then we'll filter the // the value providers to just that source, and see if we can find a matching prefix // (see CanBindValue). // // If a property does not have a binding source, then it's fair game for any value provider. // // Bottom line, if any property meets the above conditions and has a value from ValueProviders, then we'll // create the model and try to bind it. Of, if ANY properties of the model have a greedy source, // then we go ahead and create it. var hasGreedyBinders = false; for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++) { var propertyMetadata = bindingContext.ModelMetadata.Properties[i]; if (!CanBindItem(bindingContext, propertyMetadata)) { continue; } // If any property can be bound from a greedy binding source, then success. var bindingSource = propertyMetadata.BindingSource; if (bindingSource != null && bindingSource.IsGreedy) { hasGreedyBinders = true; continue; } // Otherwise, check whether the (perhaps filtered) value providers have a match. var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope( modelMetadata: propertyMetadata, fieldName: fieldName, modelName: modelName, model: null)) { // If any property can be bound from a value provider, then success. if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return(ValueProviderDataAvailable); } } } if (performsConstructorBinding) { var parameters = bindingContext.ModelMetadata.BoundConstructor.BoundConstructorParameters; for (var i = 0; i < parameters.Count; i++) { var parameterMetadata = parameters[i]; if (!CanBindItem(bindingContext, parameterMetadata)) { continue; } // If any parameter can be bound from a greedy binding source, then success. var bindingSource = parameterMetadata.BindingSource; if (bindingSource != null && bindingSource.IsGreedy) { hasGreedyBinders = true; continue; } // Otherwise, check whether the (perhaps filtered) value providers have a match. var fieldName = parameterMetadata.BinderModelName ?? parameterMetadata.ParameterName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope( modelMetadata: parameterMetadata, fieldName: fieldName, modelName: modelName, model: null)) { // If any parameter can be bound from a value provider, then success. if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return(ValueProviderDataAvailable); } } } } if (hasGreedyBinders) { return(GreedyPropertiesMayHaveData); } _logger.CannotBindToComplexType(bindingContext); return(NoDataAvailable); }
// Internal for testing. internal async Task <CollectionResult> BindComplexCollectionFromIndexes( ModelBindingContext bindingContext, IEnumerable <string> indexNames) { bool indexNamesIsFinite; if (indexNames != null) { indexNamesIsFinite = true; } else { // Read all form Values IFormCollection collection = await bindingContext.HttpContext.Request.ReadFormAsync(); List <int> indexValues = GetIndexesForCollection(collection, bindingContext.ModelName); if (indexValues.Count > 0) { indexNamesIsFinite = true; indexNames = indexValues.Select(i => i.ToString(CultureInfo.InvariantCulture)); } else { indexNamesIsFinite = false; indexNames = Enumerable.Range(0, int.MaxValue) .Select(i => i.ToString(CultureInfo.InvariantCulture)); } } var elementMetadata = bindingContext.ModelMetadata.ElementMetadata; var boundCollection = new List <TElement>(); List <TElement> modelList = (List <TElement>)bindingContext.Model; // The loop where we look for elements of all indexes foreach (var indexName in indexNames) { var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName); // Gets the current model for the given Index TElement currentModel = default(TElement); int currentModelIndex = Convert.ToInt32(indexName); if (modelList != null && modelList.Count > currentModelIndex) { currentModel = modelList[Convert.ToInt32(indexName)]; } var didBind = false; object boundValue = null; ModelBindingResult?result; using (bindingContext.EnterNestedScope( elementMetadata, fieldName: indexName, modelName: fullChildName, model: currentModel)) { // Call the binder that will bind the complex or simple type of the collection. await ElementBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } if (result != null && result.Value.IsModelSet) { didBind = true; boundValue = result.Value.Model; // Add any new element to the current List. Expects that the first new index is first avaiable index. if (currentModelIndex >= modelList.Count) { modelList.Add((TElement)boundValue); } } // infinite size collection stops on first bind failure if (!didBind && !indexNamesIsFinite) { break; } boundCollection.Add(CastOrDefault <TElement>(boundValue)); } return(new CollectionResult { Model = boundCollection, // If we're working with a fixed set of indexes then this is the format like: // // ?parameter.index=zero,one,two¶meter[zero]=0&¶meter[one]=1¶meter[two]=2... // // We need to provide this data to the validation system so it can 'replay' the keys. // But we can't just set ValidationState here, because it needs the 'real' model. ValidationStrategy = indexNamesIsFinite ? new ExplicitIndexCollectionValidationStrategy(indexNames) : null, }); }
private async Task BindModelCoreAsync(ModelBindingContext bindingContext, ViewConfigure viewConfigure) { if (bindingContext.Model == null) { bindingContext.Model = CreateModel(bindingContext); } for (var i = 0; i < bindingContext.ModelMetadata.Properties.Count; i++) { var property = bindingContext.ModelMetadata.Properties[i]; if (!CanBindProperty(bindingContext, property)) { continue; } object propertyModel = null; if (property.PropertyGetter != null && property.IsComplexType && !property.ModelType.IsArray) { propertyModel = property.PropertyGetter(bindingContext.Model); } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: property, fieldName: fieldName, modelName: modelName, model: propertyModel)) { await _modelBinderProviderContext.CreateBinder(property).BindModelAsync(bindingContext); result = bindingContext.Result; } if (result.IsModelSet) { SetProperty(bindingContext, modelName, property, result, viewConfigure); } else { var descriptor = viewConfigure.GetViewPortDescriptor(modelName); if (descriptor != null && bindingContext.ModelState.ContainsKey(modelName)) { foreach (var valid in descriptor.Validator) { if (!valid.Validate(bindingContext.ModelState[modelName].RawValue)) { valid.DisplayName = descriptor.DisplayName; bindingContext.ModelState[modelName].Errors.Clear(); bindingContext.ModelState.TryAddModelError(modelName, valid.ErrorMessage); break; } } } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } } bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); }
private async Task BindModelCoreAsync(ModelBindingContext bindingContext) { // Create model first (if necessary) to avoid reporting errors about properties when activation fails. if (bindingContext.Model == null) { bindingContext.Model = CreateModel(bindingContext); } foreach (var property in bindingContext.ModelMetadata.Properties) { if (!CanBindProperty(bindingContext, property)) { continue; } // Pass complex (including collection) values down so that binding system does not unnecessarily // recreate instances or overwrite inner properties that are not bound. No need for this with simple // values because they will be overwritten if binding succeeds. Arrays are never reused because they // cannot be resized. object propertyModel = null; if (property.PropertyGetter != null && property.IsComplexType && !property.ModelType.IsArray) { propertyModel = property.PropertyGetter(bindingContext.Model); } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: property, fieldName: fieldName, modelName: modelName, model: propertyModel)) { await BindProperty(bindingContext); result = bindingContext.Result ?? ModelBindingResult.Failed(modelName); } if (result.IsModelSet) { SetProperty(bindingContext, property, result); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, bindingContext.Model); }
/// <inheritdoc /> public override async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } await base.BindModelAsync(bindingContext); if (!bindingContext.Result.IsModelSet) { // No match for the prefix at all. return; } var result = bindingContext.Result; Debug.Assert(result.Model != null); var model = (IDictionary <TKey, TValue>)result.Model; if (model.Count != 0) { // ICollection<KeyValuePair<TKey, TValue>> approach was successful. return; } Logger.NoKeyValueFormatForDictionaryModelBinder(bindingContext); if (!(bindingContext.ValueProvider is IEnumerableValueProvider enumerableValueProvider)) { // No IEnumerableValueProvider available for the fallback approach. For example the user may have // replaced the ValueProvider with something other than a CompositeValueProvider. if (bindingContext.IsTopLevelObject) { AddErrorIfBindingRequired(bindingContext); } return; } // Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first. var prefix = bindingContext.ModelName; var keys = enumerableValueProvider.GetKeysFromPrefix(prefix); if (keys.Count == 0) { // No entries with the expected keys. if (bindingContext.IsTopLevelObject) { AddErrorIfBindingRequired(bindingContext); } return; } // Update the existing successful but empty ModelBindingResult. var elementMetadata = bindingContext.ModelMetadata.ElementMetadata; var valueMetadata = elementMetadata.Properties[nameof(KeyValuePair <TKey, TValue> .Value)]; var keyMappings = new Dictionary <string, TKey>(StringComparer.Ordinal); foreach (var kvp in keys) { // Use InvariantCulture to convert the key since ExpressionHelper.GetExpressionText() would use // that culture when rendering a form. var convertedKey = ModelBindingHelper.ConvertTo <TKey>(kvp.Key, culture: null); using (bindingContext.EnterNestedScope( modelMetadata: valueMetadata, fieldName: bindingContext.FieldName, modelName: kvp.Value, model: null)) { await _valueBinder.BindModelAsync(bindingContext); var valueResult = bindingContext.Result; if (!valueResult.IsModelSet) { // Factories for IKeyRewriterValueProvider implementations are not all-or-nothing i.e. // "[key][propertyName]" may be rewritten as ".key.propertyName" or "[key].propertyName". Try // again in case this scope is binding a complex type and rewriting // landed on ".key.propertyName" or in case this scope is binding another collection and an // IKeyRewriterValueProvider implementation was first (hiding the original "[key][next key]"). if (kvp.Value.EndsWith("]")) { bindingContext.ModelName = ModelNames.CreatePropertyModelName(prefix, kvp.Key); } else { bindingContext.ModelName = ModelNames.CreateIndexModelName(prefix, kvp.Key); } await _valueBinder.BindModelAsync(bindingContext); valueResult = bindingContext.Result; } // Always add an entry to the dictionary but validate only if binding was successful. model[convertedKey] = ModelBindingHelper.CastOrDefault <TValue>(valueResult.Model); keyMappings.Add(bindingContext.ModelName, convertedKey); } } bindingContext.ValidationState.Add(model, new ValidationStateEntry() { Strategy = new ShortFormDictionaryValidationStrategy <TKey, TValue>(keyMappings, valueMetadata), }); }
internal async Task <CollectionResult> BindComplexCollectionFromIndexes( ModelBindingContext bindingContext, IEnumerable <string>?indexNames) { bool indexNamesIsFinite; if (indexNames != null) { indexNamesIsFinite = true; } else { indexNamesIsFinite = false; var limit = _maxModelBindingCollectionSize == int.MaxValue ? int.MaxValue : _maxModelBindingCollectionSize + 1; indexNames = Enumerable .Range(0, limit) .Select(i => i.ToString(CultureInfo.InvariantCulture)); } var elementMetadata = bindingContext.ModelMetadata.ElementMetadata !; var boundCollection = new List <TElement?>(); foreach (var indexName in indexNames) { var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName); ModelBindingResult?result; using (bindingContext.EnterNestedScope( elementMetadata, fieldName: indexName, modelName: fullChildName, model: null)) { await ElementBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } var didBind = false; object?boundValue = null; if (result != null && result.Value.IsModelSet) { didBind = true; boundValue = result.Value.Model; } // infinite size collection stops on first bind failure if (!didBind && !indexNamesIsFinite) { break; } boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue)); } // Did the collection grow larger than the limit? if (boundCollection.Count > _maxModelBindingCollectionSize) { // Look for a non-empty name. Both ModelName and OriginalModelName may be empty at the top level. var name = string.IsNullOrEmpty(bindingContext.ModelName) ? (string.IsNullOrEmpty(bindingContext.OriginalModelName) && bindingContext.ModelMetadata.MetadataKind != ModelMetadataKind.Type ? bindingContext.ModelMetadata.Name : bindingContext.OriginalModelName) : // This name may unfortunately be empty. bindingContext.ModelName; throw new InvalidOperationException(Resources.FormatModelBinding_ExceededMaxModelBindingCollectionSize( name, nameof(MvcOptions), nameof(MvcOptions.MaxModelBindingCollectionSize), _maxModelBindingCollectionSize, bindingContext.ModelMetadata.ElementType)); } return(new CollectionResult(boundCollection) { // If we're working with a fixed set of indexes then this is the format like: // // ?parameter.index=zero¶meter.index=one¶meter.index=two¶meter[zero]=0¶meter[one]=1¶meter[two]=2... // // We need to provide this data to the validation system so it can 'replay' the keys. // But we can't just set ValidationState here, because it needs the 'real' model. ValidationStrategy = indexNamesIsFinite ? new ExplicitIndexCollectionValidationStrategy(indexNames) : null, }); }
// Internal for testing. internal async Task <CollectionResult> BindComplexCollectionFromIndexes( ModelBindingContext bindingContext, IEnumerable <string> indexNames) { bool indexNamesIsFinite; if (indexNames != null) { indexNamesIsFinite = true; } else { indexNamesIsFinite = false; indexNames = Enumerable.Range(0, int.MaxValue) .Select(i => i.ToString(CultureInfo.InvariantCulture)); } var elementMetadata = bindingContext.ModelMetadata.ElementMetadata; var boundCollection = new List <TElement>(); foreach (var indexName in indexNames) { var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName); ModelBindingResult?result; using (bindingContext.EnterNestedScope( elementMetadata, fieldName: indexName, modelName: fullChildName, model: null)) { await ElementBinder.BindModelAsync(bindingContext); result = bindingContext.Result; } var didBind = false; object boundValue = null; if (result != null && result.Value.IsModelSet) { didBind = true; boundValue = result.Value.Model; } // infinite size collection stops on first bind failure if (!didBind && !indexNamesIsFinite) { break; } boundCollection.Add(CastOrDefault <TElement>(boundValue)); } return(new CollectionResult { Model = boundCollection, // If we're working with a fixed set of indexes then this is the format like: // // ?parameter.index=zero¶meter.index=one¶meter.index=two¶meter[zero]=0¶meter[one]=1¶meter[two]=2... // // We need to provide this data to the validation system so it can 'replay' the keys. // But we can't just set ValidationState here, because it needs the 'real' model. ValidationStrategy = indexNamesIsFinite ? new ExplicitIndexCollectionValidationStrategy(indexNames) : null, }); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var itemName = bindingContext.FieldName; // check if we can get the item from the RouteData var rvd = bindingContext.ActionContext.RouteData.Values; if (rvd.ContainsKey(itemName) && bindingContext.ModelType.IsAssignableFrom(rvd[itemName].GetType())) { bindingContext.Model = rvd[itemName]; if (this.propertyBinders == null || bindingContext.BindingSource != BindingSource.Body) { // we aren't meant to bind request data onto the value from RouteData bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); return; } // Check whether the underlying type of the data is different from the model type if (rvd[itemName].GetType() != bindingContext.ModelType) { // if so recurse on model binding with the underlying model type var servProv = bindingContext.ActionContext.HttpContext.RequestServices; var metadataProvider = servProv.GetRequiredService <IModelMetadataProvider>(); var modelMetadata = metadataProvider.GetMetadataForType(bindingContext.Model.GetType()); bindingContext.ModelMetadata = modelMetadata; var modelBinderFactory = servProv.GetRequiredService <IModelBinderFactory>(); var factoryContext = new ModelBinderFactoryContext() { Metadata = modelMetadata, BindingInfo = new BindingInfo() { BinderModelName = modelMetadata.BinderModelName, BinderType = modelMetadata.BinderType, BindingSource = modelMetadata.BindingSource, PropertyFilterProvider = modelMetadata.PropertyFilterProvider, }, // We're using the model metadata as the cache token here so that TryUpdateModelAsync calls // for the same model type can share a binder. This won't overlap with normal model binding // operations because they use the ParameterDescriptor for the token. CacheToken = modelMetadata }; var underlyingBinder = modelBinderFactory.CreateBinder(factoryContext); await underlyingBinder.BindModelAsync(bindingContext); return; } } if (bindingContext.IsTopLevelObject) { // We know we are editing the data, so we empty all collections, as if there // are no collection items present in the posted data, this means the collection is empty new ContentBindingPreparer().Visit(bindingContext.Model); bindingContext.ModelName = ""; } if (!CanCreateModel(bindingContext)) { return; } //Create model first(if necessary) to avoid reporting errors about properties when activation fails. //if (bindingContext.Model == null) //{ // bindingContext.Model = CreateModel(bindingContext); //} foreach (var property in bindingContext.ModelMetadata.Properties) { if (!CanBindProperty(bindingContext, property)) { continue; } // Pass complex (including collection) values down so that binding system does not unnecessarily // recreate instances or overwrite inner properties that are not bound. No need for this with simple // values because they will be overwritten if binding succeeds. Arrays are never reused because they // cannot be resized. object propertyModel = null; if (property.PropertyGetter != null && property.IsComplexType && !property.ModelType.IsArray) { propertyModel = property.PropertyGetter(bindingContext.Model); } var fieldName = property.BinderModelName ?? property.PropertyName; var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName); ModelBindingResult result; using (bindingContext.EnterNestedScope( modelMetadata: property, fieldName: fieldName, modelName: modelName, model: propertyModel)) { await BindProperty(bindingContext); result = bindingContext.Result; } if (result.IsModelSet) { SetProperty(bindingContext, modelName, property, result); } else if (property.IsBindingRequired) { var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName); bindingContext.ModelState.TryAddModelError(modelName, message); } } bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); }
/// <inheritdoc /> public override async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } await base.BindModelAsync(bindingContext); if (bindingContext.Result == null || !bindingContext.Result.Value.IsModelSet) { // No match for the prefix at all. return; } var result = bindingContext.Result.Value; Debug.Assert(result.Model != null); var model = (IDictionary <TKey, TValue>)result.Model; if (model.Count != 0) { // ICollection<KeyValuePair<TKey, TValue>> approach was successful. return; } var enumerableValueProvider = bindingContext.ValueProvider as IEnumerableValueProvider; if (enumerableValueProvider == null) { // No IEnumerableValueProvider available for the fallback approach. For example the user may have // replaced the ValueProvider with something other than a CompositeValueProvider. return; } // Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first. var keys = enumerableValueProvider.GetKeysFromPrefix(bindingContext.ModelName); if (keys.Count == 0) { // No entries with the expected keys. return; } // Update the existing successful but empty ModelBindingResult. var elementMetadata = bindingContext.ModelMetadata.ElementMetadata; var valueMetadata = elementMetadata.Properties[nameof(KeyValuePair <TKey, TValue> .Value)]; var keyMappings = new Dictionary <string, TKey>(StringComparer.Ordinal); foreach (var kvp in keys) { // Use InvariantCulture to convert the key since ExpressionHelper.GetExpressionText() would use // that culture when rendering a form. var convertedKey = ModelBindingHelper.ConvertTo <TKey>(kvp.Key, culture: null); using (bindingContext.EnterNestedScope( modelMetadata: valueMetadata, fieldName: bindingContext.FieldName, modelName: kvp.Value, model: null)) { await _valueBinder.BindModelAsync(bindingContext); var valueResult = bindingContext.Result; // Always add an entry to the dictionary but validate only if binding was successful. model[convertedKey] = ModelBindingHelper.CastOrDefault <TValue>(valueResult.Value.Model); keyMappings.Add(kvp.Key, convertedKey); } } bindingContext.ValidationState.Add(model, new ValidationStateEntry() { Strategy = new ShortFormDictionaryValidationStrategy <TKey, TValue>(keyMappings, valueMetadata), }); }
private bool CanValueBindAnyModelProperties(ModelBindingContext bindingContext) { // If there are no properties on the model, there is nothing to bind. We are here means this is not a top // level object. So we return false. if (bindingContext.ModelMetadata.Properties.Count == 0) { return false; } // We want to check to see if any of the properties of the model can be bound using the value providers, // because that's all that MutableObjectModelBinder can handle. // // However, because a property might specify a custom binding source ([FromForm]), it's not correct // for us to just try bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName), // because that may include other value providers - that would lead us to mistakenly create the model // when the data is coming from a source we should use (ex: value found in query string, but the // model has [FromForm]). // // To do this we need to enumerate the properties, and see which of them provide a binding source // through metadata, then we decide what to do. // // If a property has a binding source, and it's a greedy source, then it's not // allowed to come from a value provider, so we skip it. // // If a property has a binding source, and it's a non-greedy source, then we'll filter the // the value providers to just that source, and see if we can find a matching prefix // (see CanBindValue). // // If a property does not have a binding source, then it's fair game for any value provider. // // If any property meets the above conditions and has a value from valueproviders, then we'll // create the model and try to bind it. OR if ALL properties of the model have a greedy source, // then we go ahead and create it. // var hasBindableProperty = false; var isAnyPropertyEnabledForValueProviderBasedBinding = false; foreach (var propertyMetadata in bindingContext.ModelMetadata.Properties) { if (!CanBindProperty(bindingContext, propertyMetadata)) { continue; } hasBindableProperty = true; // This check will skip properties which are marked explicitly using a non value binder. var bindingSource = propertyMetadata.BindingSource; if (bindingSource == null || !bindingSource.IsGreedy) { isAnyPropertyEnabledForValueProviderBasedBinding = true; var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName; var modelName = ModelNames.CreatePropertyModelName( bindingContext.ModelName, fieldName); using (bindingContext.EnterNestedScope( modelMetadata: propertyMetadata, fieldName: fieldName, modelName: modelName, model: null)) { // If any property can return a true value. if (CanBindValue(bindingContext)) { return true; } } } } if (hasBindableProperty && !isAnyPropertyEnabledForValueProviderBasedBinding) { // All the properties are marked with a non value provider based marker like [FromHeader] or // [FromBody]. return true; } return false; }