Exemplo n.º 1
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));

            var innerBindingContext = ModelBindingContext.CreateChildBindingContext(
                bindingContext,
                elementMetadata,
                fieldName: bindingContext.FieldName,
                modelName: bindingContext.ModelName,
                model: null);

            foreach (var value in values)
            {
                innerBindingContext.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;
                var    result     =
                    await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(innerBindingContext);

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

            return(new CollectionResult
            {
                Model = boundCollection
            });
        }
Exemplo n.º 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 <IEnumerable <TElement> > 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 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)
                {
                    boundValue = result.Model;
                }
                boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue));
            }

            return(boundCollection);
        }
Exemplo n.º 3
0
        internal async Task <BindResult <TModel> > TryBindStrongModel <TModel>(ModelBindingContext parentBindingContext,
                                                                               string propertyName)
        {
            var propertyModelMetadata =
                parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null,
                                                                                                 modelType: typeof(TModel));
            var propertyModelName =
                ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
            var propertyBindingContext =
                new ModelBindingContext(parentBindingContext, propertyModelName, propertyModelMetadata);

            if (await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
            {
                var untypedModel = propertyBindingContext.Model;
                var model        = ModelBindingHelper.CastOrDefault <TModel>(untypedModel);
                parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode);
                return(new BindResult <TModel>(success: true, model: model));
            }

            return(new BindResult <TModel>(success: false, model: default(TModel)));
        }
Exemplo n.º 4
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 <List <TElement> > BindSimpleCollection(ModelBindingContext bindingContext,
                                                                    object rawValue,
                                                                    CultureInfo culture)
        {
            if (rawValue == null)
            {
                return(null); // nothing to do
            }

            var boundCollection = new List <TElement>();

            var rawValueArray = RawValueToObjectArray(rawValue);

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

                object boundValue = null;
                if (await bindingContext.ModelBinder.BindModelAsync(innerBindingContext))
                {
                    boundValue = innerBindingContext.Model;
                    bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode);
                }
                boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue));
            }

            return(boundCollection);
        }
Exemplo n.º 5
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 metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var elementMetadata  = metadataProvider.GetMetadataForType(typeof(TElement));

            var boundCollection = new List <TElement>();
            var validationNode  = new ModelValidationNode(
                bindingContext.ModelName,
                bindingContext.ModelMetadata,
                boundCollection);

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

                var    didBind    = false;
                object boundValue = null;

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

                if (result != null && result.IsModelSet)
                {
                    didBind    = true;
                    boundValue = result.Model;
                    if (result.ValidationNode != null)
                    {
                        validationNode.ChildNodes.Add(result.ValidationNode);
                    }
                }

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

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

            return(new CollectionResult
            {
                ValidationNode = validationNode,
                Model = boundCollection
            });
        }
Exemplo n.º 6
0
        /// <inheritdoc />
        public override async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
        {
            var result = await base.BindModelAsync(bindingContext);

            if (!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 = enumerableValueProvider.GetKeysFromPrefix(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.CreateChildBindingContext(
                bindingContext,
                valueMetadata,
                fieldName: bindingContext.FieldName,
                modelName: bindingContext.ModelName,
                model: null);

            var modelBinder = bindingContext.OperationBindingContext.ModelBinder;

            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);

                valueBindingContext.ModelName = kvp.Value;

                var valueResult = await modelBinder.BindModelAsync(valueBindingContext);

                // Always add an entry to the dictionary but validate only if binding was successful.
                model[convertedKey] = ModelBindingHelper.CastOrDefault <TValue>(valueResult.Model);
                keyMappings.Add(kvp.Key, convertedKey);
            }

            bindingContext.ValidationState.Add(model, new ValidationStateEntry()
            {
                Strategy = new ShortFormDictionaryValidationStrategy <TKey, TValue>(keyMappings, valueMetadata),
            });

            return(result);
        }
Exemplo n.º 7
0
        public async Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
        {
            ModelBindingHelper.ValidateBindingContext(bindingContext,
                                                      typeof(KeyValuePair <TKey, TValue>),
                                                      allowNullModel: true);

            var childNodes = new List <ModelValidationNode>();
            var keyResult  = await TryBindStrongModel <TKey>(bindingContext, "Key", childNodes);

            var valueResult = await TryBindStrongModel <TValue>(bindingContext, "Value", childNodes);

            if (keyResult.IsModelSet && valueResult.IsModelSet)
            {
                var model = new KeyValuePair <TKey, TValue>(
                    ModelBindingHelper.CastOrDefault <TKey>(keyResult.Model),
                    ModelBindingHelper.CastOrDefault <TValue>(valueResult.Model));

                // Update the model for the top level validation node.
                var modelValidationNode =
                    new ModelValidationNode(
                        bindingContext.ModelName,
                        bindingContext.ModelMetadata,
                        model,
                        childNodes);

                // Success
                return(new ModelBindingResult(
                           model,
                           bindingContext.ModelName,
                           isModelSet: true,
                           validationNode: modelValidationNode));
            }
            else if (!keyResult.IsModelSet && valueResult.IsModelSet)
            {
                bindingContext.ModelState.TryAddModelError(
                    keyResult.Key,
                    Resources.KeyValuePair_BothKeyAndValueMustBePresent);

                // Were able to get some data for this model.
                // Always tell the model binding system to skip other model binders i.e. return non-null.
                return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false));
            }
            else if (keyResult.IsModelSet && !valueResult.IsModelSet)
            {
                bindingContext.ModelState.TryAddModelError(
                    valueResult.Key,
                    Resources.KeyValuePair_BothKeyAndValueMustBePresent);

                // Were able to get some data for this model.
                // Always tell the model binding system to skip other model binders i.e. return non-null.
                return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false));
            }
            else
            {
                // If this is the fallback case, and we failed to find data as a top-level model, then generate a
                // default 'empty' model and return it.
                var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null;
                var hasExplicitAlias = bindingContext.BinderModelName != null;

                if (isTopLevelObject && (hasExplicitAlias || bindingContext.ModelName == string.Empty))
                {
                    var model = new KeyValuePair <TKey, TValue>();

                    var validationNode = new ModelValidationNode(
                        bindingContext.ModelName,
                        bindingContext.ModelMetadata,
                        model);

                    return(new ModelBindingResult(
                               model,
                               bindingContext.ModelName,
                               isModelSet: true,
                               validationNode: validationNode));
                }

                return(null);
            }
        }
Exemplo n.º 8
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 metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var elementMetadata  = metadataProvider.GetMetadataForType(typeof(TElement));

            var boundCollection = new List <TElement>();

            foreach (var indexName in indexNames)
            {
                var fullChildName       = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName);
                var childBindingContext = ModelBindingContext.CreateChildBindingContext(
                    bindingContext,
                    elementMetadata,
                    fieldName: indexName,
                    modelName: fullChildName,
                    model: null);


                var    didBind    = false;
                object boundValue = null;

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

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

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

                boundCollection.Add(ModelBindingHelper.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,
            });
        }
Exemplo n.º 9
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);
        }