コード例 #1
0
        internal async Task <ModelBindingResult> TryBindStrongModel <TModel>(ModelBindingContext parentBindingContext,
                                                                             string propertyName)
        {
            var propertyModelMetadata =
                parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
            var propertyModelName =
                ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
            var propertyBindingContext = ModelBindingContext.GetChildModelBindingContext(
                parentBindingContext,
                propertyModelName,
                propertyModelMetadata);

            propertyBindingContext.BinderModelName = propertyModelMetadata.BinderModelName;

            var modelBindingResult = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
                propertyBindingContext);

            if (modelBindingResult != null)
            {
                return(modelBindingResult);
            }

            // Always return a ModelBindingResult to avoid an NRE in BindModelAsync.
            return(new ModelBindingResult(model: default(TModel), key: propertyModelName, isModelSet: false));
        }
コード例 #2
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 async Task <CollectionResult> BindSimpleCollection(
            ModelBindingContext bindingContext,
            object rawValue,
            CultureInfo culture)
        {
            if (rawValue == null)
            {
                return(null); // nothing to do
            }

            var boundCollection = new List <TElement>();

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var elementMetadata  = metadataProvider.GetMetadataForType(typeof(TElement));

            var validationNode = new ModelValidationNode(
                bindingContext.ModelName,
                bindingContext.ModelMetadata,
                boundCollection);
            var rawValueArray = RawValueToObjectArray(rawValue);

            foreach (var rawValueElement in rawValueArray)
            {
                var innerBindingContext = ModelBindingContext.GetChildModelBindingContext(
                    bindingContext,
                    bindingContext.ModelName,
                    elementMetadata);
                innerBindingContext.ValueProvider = new CompositeValueProvider
                {
                    // our temporary provider goes at the front of the list
                    new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture),
                    bindingContext.ValueProvider
                };

                object boundValue = null;
                var    result     =
                    await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(innerBindingContext);

                if (result != null && result.IsModelSet)
                {
                    boundValue = result.Model;
                    if (result.ValidationNode != null)
                    {
                        validationNode.ChildNodes.Add(result.ValidationNode);
                    }
                }
                boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue));
            }

            return(new CollectionResult
            {
                ValidationNode = validationNode,
                Model = boundCollection
            });
        }
コード例 #3
0
        internal async Task <IEnumerable <TElement> > BindComplexCollectionFromIndexes(ModelBindingContext bindingContext,
                                                                                       IEnumerable <string> indexNames)
        {
            bool indexNamesIsFinite;

            if (indexNames != null)
            {
                indexNamesIsFinite = true;
            }
            else
            {
                indexNamesIsFinite = false;
                indexNames         = Enumerable.Range(0, Int32.MaxValue)
                                     .Select(i => i.ToString(CultureInfo.InvariantCulture));
            }

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var elementMetadata  = metadataProvider.GetMetadataForType(typeof(TElement));

            var boundCollection = new List <TElement>();

            foreach (var indexName in indexNames)
            {
                var fullChildName       = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName);
                var childBindingContext = ModelBindingContext.GetChildModelBindingContext(
                    bindingContext,
                    fullChildName,
                    elementMetadata);

                var    didBind    = false;
                object boundValue = null;

                var modelType = bindingContext.ModelType;

                var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childBindingContext);

                if (result != null)
                {
                    didBind    = true;
                    boundValue = result.Model;
                }

                // infinite size collection stops on first bind failure
                if (!didBind && !indexNamesIsFinite)
                {
                    break;
                }

                boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue));
            }

            return(boundCollection);
        }
コード例 #4
0
        // Returned dictionary contains entries corresponding to properties against which binding was attempted. If
        // binding failed, the entry's value will have IsModelSet == false. Binding is attempted for all elements of
        // propertyMetadatas.
        private async Task <IDictionary <ModelMetadata, ModelBindingResult> > BindPropertiesAsync(
            ModelBindingContext bindingContext,
            IEnumerable <ModelMetadata> propertyMetadatas)
        {
            var results = new Dictionary <ModelMetadata, ModelBindingResult>();

            foreach (var propertyMetadata in propertyMetadatas)
            {
                var propertyModelName = ModelNames.CreatePropertyModelName(
                    bindingContext.ModelName,
                    propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName);
                var childContext = ModelBindingContext.GetChildModelBindingContext(
                    bindingContext,
                    propertyModelName,
                    propertyMetadata);

                // ModelBindingContext.Model property values may be non-null when invoked via TryUpdateModel(). 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.
                //
                // ModelMetadata.PropertyGetter is not null safe; use it only if Model is non-null.
                if (bindingContext.Model != null &&
                    propertyMetadata.PropertyGetter != null &&
                    propertyMetadata.IsComplexType &&
                    !propertyMetadata.ModelType.IsArray)
                {
                    childContext.Model = propertyMetadata.PropertyGetter(bindingContext.Model);
                }

                var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childContext);

                if (result == null)
                {
                    // Could not bind. Let ProcessResult() know explicitly.
                    result = new ModelBindingResult(model: null, key: propertyModelName, isModelSet: false);
                }

                results[propertyMetadata] = result;
            }

            return(results);
        }
コード例 #5
0
        private async Task <ModelBindingResult> CreateAndPopulateDto(
            ModelBindingContext bindingContext,
            IEnumerable <ModelMetadata> propertyMetadatas)
        {
            // create a DTO and call into the DTO binder
            var dto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var dtoMetadata      = metadataProvider.GetMetadataForType(typeof(ComplexModelDto));

            var childContext = ModelBindingContext.GetChildModelBindingContext(
                bindingContext,
                bindingContext.ModelName,
                dtoMetadata);

            childContext.Model = dto;

            return(await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childContext));
        }
コード例 #6
0
        public async Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(ComplexModelDto))
            {
                return(null);
            }

            ModelBindingHelper.ValidateBindingContext(bindingContext, typeof(ComplexModelDto), allowNullModel: false);

            var dto = (ComplexModelDto)bindingContext.Model;

            foreach (var propertyMetadata in dto.PropertyMetadata)
            {
                var propertyModelName = ModelNames.CreatePropertyModelName(
                    bindingContext.ModelName,
                    propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName);

                var propertyBindingContext = ModelBindingContext.GetChildModelBindingContext(
                    bindingContext,
                    propertyModelName,
                    propertyMetadata);

                var modelBindingResult =
                    await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext);

                if (modelBindingResult == null)
                {
                    // Could not bind. Add a result so MutableObjectModelBinder will check for [DefaultValue].
                    dto.Results[propertyMetadata] =
                        new ModelBindingResult(model: null, key: propertyModelName, isModelSet: false);
                }
                else
                {
                    dto.Results[propertyMetadata] = modelBindingResult;
                }
            }

            return(new ModelBindingResult(dto, bindingContext.ModelName, isModelSet: true));
        }
コード例 #7
0
        private async Task <bool> CanValueBindAnyModelProperties(MutableObjectBinderContext context)
        {
            // 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 ALL 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 isAnyPropertyEnabledForValueProviderBasedBinding = false;

            foreach (var propertyMetadata in context.PropertyMetadata)
            {
                // 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 propertyModelName = ModelNames.CreatePropertyModelName(
                        context.ModelBindingContext.ModelName,
                        propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName);

                    var propertyModelBindingContext = ModelBindingContext.GetChildModelBindingContext(
                        context.ModelBindingContext,
                        propertyModelName,
                        propertyMetadata);

                    // If any property can return a true value.
                    if (await CanBindValue(propertyModelBindingContext))
                    {
                        return(true);
                    }
                }
            }

            if (!isAnyPropertyEnabledForValueProviderBasedBinding)
            {
                // Either there are no properties or all the properties are marked as
                // a non value provider based marker.
                // This would be the case when the model has all its properties annotated with
                // a IBinderMetadata. We want to be able to create such a model.
                return(true);
            }

            return(false);
        }
コード例 #8
0
        /// <inheritdoc />
        public override async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
        {
            var result = await base.BindModelAsync(bindingContext);

            if (result == null || !result.IsModelSet)
            {
                // No match for the prefix at all.
                return(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(result);
            }

            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(result);
            }

            // Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first.
            var keys = await enumerableValueProvider.GetKeysFromPrefixAsync(bindingContext.ModelName);

            if (!keys.Any())
            {
                // No entries with the expected keys.
                return(result);
            }

            // Update the existing successful but empty ModelBindingResult.
            var metadataProvider    = bindingContext.OperationBindingContext.MetadataProvider;
            var valueMetadata       = metadataProvider.GetMetadataForType(typeof(TValue));
            var valueBindingContext = ModelBindingContext.GetChildModelBindingContext(
                bindingContext,
                bindingContext.ModelName,
                valueMetadata);

            var modelBinder    = bindingContext.OperationBindingContext.ModelBinder;
            var validationNode = result.ValidationNode;

            foreach (var key in keys)
            {
                var dictionaryKey = ConvertFromString(key.Key);
                valueBindingContext.ModelName = key.Value;

                var valueResult = await modelBinder.BindModelAsync(valueBindingContext);

                // Always add an entry to the dictionary but validate only if binding was successful.
                model[dictionaryKey] = ModelBindingHelper.CastOrDefault <TValue>(valueResult?.Model);
                if (valueResult != null && valueResult.IsModelSet)
                {
                    validationNode.ChildNodes.Add(valueResult.ValidationNode);
                }
            }

            return(result);
        }