コード例 #1
0
        /// <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.Any())
            {
                // No entries with the expected keys.
                return;
            }

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

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

                using (bindingContext.EnterNestedScope(
                           modelMetadata: valueMetadata,
                           fieldName: bindingContext.FieldName,
                           modelName: kvp.Value,
                           model: null))
                {
                    await modelBinder.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),
            });
        }
コード例 #2
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                didBind    = false;
                object             boundValue = null;
                ModelBindingResult?result;
                using (bindingContext.EnterNestedScope(
                           elementMetadata,
                           fieldName: indexName,
                           modelName: fullChildName,
                           model: null))
                {
                    await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);

                    result = bindingContext.Result;
                }

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

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