/// <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;
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
            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);
            }
Beispiel #6
0
        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);
        }
Beispiel #7
0
 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);
        }
Beispiel #12
0
    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);
    }
Beispiel #13
0
        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);
        }
Beispiel #15
0
        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);
        }
Beispiel #16
0
        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);
            }
        }
Beispiel #19
0
        // 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
            });
        }
Beispiel #20
0
        // 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);
        }
Beispiel #23
0
        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);
        }
Beispiel #24
0
        // 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&parameter[zero]=0&&parameter[one]=1&parameter[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&parameter.index=one&parameter.index=two&parameter[zero]=0&parameter[one]=1&parameter[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,
        });
    }
Beispiel #29
0
        // 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&parameter.index=one&parameter.index=two&parameter[zero]=0&parameter[one]=1&parameter[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,
            });
        }
Beispiel #30
0
        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;
        }