Beispiel #1
0
        private void BindCollection(ModelStateDictionary modelState, IValueProvider valueProvider, string modelName, DataSet dataSet)
        {
            var indexNames = GetIndexNames(valueProvider, modelName);

            bool indexNamesIsFinite;

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

            var currentIndex = 0;

            foreach (var indexName in indexNames)
            {
                var dataRowModelName = ModelNames.CreateIndexModelName(modelName, indexName);
                if (!valueProvider.ContainsPrefix(dataRowModelName) && !indexNamesIsFinite)
                {
                    break;
                }

                if (currentIndex >= dataSet.Count)
                {
                    dataSet.AddRow();
                }
                Bind(modelState, valueProvider, dataRowModelName, dataSet[currentIndex++]);
            }
        }
            public bool MoveNext()
            {
                TValue value;

                while (true)
                {
                    if (!_keyMappingEnumerator.MoveNext())
                    {
                        return(false);
                    }

                    if (_model.TryGetValue(_keyMappingEnumerator.Current.Value, out value))
                    {
                        // Skip over entries that we can't find in the dictionary, they will show up as unvalidated.
                        break;
                    }
                }

                var key   = ModelNames.CreateIndexModelName(_key, _keyMappingEnumerator.Current.Key);
                var model = value;

                _entry = new ValidationEntry(_metadata, key, model);

                return(true);
            }
Beispiel #3
0
        private static string ResolveMemberName(DataSet dataSet, bool isScalar, Column column, DataRow dataRow)
        {
            if (column.GetParent() == dataSet.Model)
            {
                return(isScalar ? column.Name : ModelNames.CreatePropertyModelName(ModelNames.CreateIndexModelName(string.Empty, dataRow.Index), column.Name));
            }

            Debug.Assert(dataRow != null);
            return(ResolveMemberName(dataSet, isScalar, dataRow, column.Name));
        }
Beispiel #4
0
 private static IEnumerable <ModelValidationResult> ValidateCollection(string prefix, DataSet dataSet)
 {
     for (int index = 0; index < dataSet.Count; index++)
     {
         var dataRowPrefix = ModelNames.CreateIndexModelName(prefix, index);
         foreach (var result in Validate(dataRowPrefix, dataSet[index]))
         {
             yield return(result);
         }
     }
 }
Beispiel #5
0
        private static string ResolveMemberName(DataSet dataSet, bool isScalar, DataRow dataRow, string memberName)
        {
            if (dataRow.Model == dataSet.Model)
            {
                return(isScalar ? memberName : ModelNames.CreatePropertyModelName(ModelNames.CreateIndexModelName(string.Empty, dataRow.Index), memberName));
            }

            memberName = ModelNames.CreatePropertyModelName(ModelNames.CreateIndexModelName(dataRow.Model.GetName(), dataRow.Index), memberName);
            Debug.Assert(dataRow.ParentDataRow != null);
            return(ResolveMemberName(dataSet, isScalar, dataRow.ParentDataRow, memberName));
        }
Beispiel #6
0
        private IList <ModelValidationNode> GetChildNodes(ValidationContext context, ModelExplorer modelExplorer)
        {
            var validationNode = context.ValidationNode;

            // This is the trivial case where the node-tree that was built-up during binding already has
            // all of the nodes we need.
            if (validationNode.ChildNodes.Count != 0 ||
                !validationNode.ValidateAllProperties ||
                validationNode.Model == null)
            {
                return(validationNode.ChildNodes);
            }

            var childNodes      = new List <ModelValidationNode>(validationNode.ChildNodes);
            var elementMetadata = modelExplorer.Metadata.ElementMetadata;

            if (elementMetadata == null)
            {
                foreach (var property in validationNode.ModelMetadata.Properties)
                {
                    var propertyExplorer    = modelExplorer.GetExplorerForProperty(property.PropertyName);
                    var propertyBindingName = property.BinderModelName ?? property.PropertyName;
                    var childKey            = ModelNames.CreatePropertyModelName(validationNode.Key, propertyBindingName);
                    var childNode           = new ModelValidationNode(childKey, property, propertyExplorer.Model)
                    {
                        ValidateAllProperties = true
                    };
                    childNodes.Add(childNode);
                }
            }
            else
            {
                var enumerableModel = (IEnumerable)modelExplorer.Model;

                // An integer index is incorrect in scenarios where there is a custom index provided by the user.
                // However those scenarios are supported by createing a ModelValidationNode with the right keys.
                var index = 0;
                foreach (var element in enumerableModel)
                {
                    var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
                    var elementKey      = ModelNames.CreateIndexModelName(validationNode.Key, index);
                    var childNode       = new ModelValidationNode(elementKey, elementMetadata, elementExplorer.Model)
                    {
                        ValidateAllProperties = true
                    };

                    childNodes.Add(childNode);
                    index++;
                }
            }

            return(childNodes);
        }
Beispiel #7
0
        private static void Visit(StringBuilder builder, HashSet <object> set, string prefix, object model)
        {
            var typeInfo = model.GetType().GetTypeInfo();

            if (!typeInfo.IsValueType && typeInfo != typeof(string).GetTypeInfo())
            {
                if (!set.Add(model))
                {
                    // Already visited this model.
                    return;
                }
            }

            if (typeInfo.Assembly == typeof(Model).GetTypeInfo().Assembly)
            {
                // This is another POCO.
                foreach (var property in typeInfo.AsType().GetRuntimeProperties())
                {
                    var value = property.GetValue(model);
                    if (value != null)
                    {
                        Visit(builder, set, ModelNames.CreatePropertyModelName(prefix, property.Name), value);
                    }
                }
            }
            else if (
                typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(typeInfo) &&
                typeInfo != typeof(string).GetTypeInfo())
            {
                var i = 0;
                foreach (var item in (IEnumerable)model)
                {
                    if (item != null)
                    {
                        Visit(builder, set, ModelNames.CreateIndexModelName(prefix, i++), item);
                    }
                }
            }
            else
            {
                FieldCount++;

                // This is a simple type.
                builder.Append(prefix);
                builder.Append("=");
                builder.Append(UrlEncoder.Default.Encode(Convert.ToString(model, CultureInfo.InvariantCulture)));
                builder.Append("&");
            }
        }
Beispiel #8
0
            public bool MoveNext()
            {
                _index++;
                if (!_enumerator.MoveNext())
                {
                    return(false);
                }

                var key   = ModelNames.CreateIndexModelName(_key, _index);
                var model = _enumerator.Current;

                _entry = new ValidationEntry(_metadata, key, model);

                return(true);
            }
Beispiel #9
0
        private void ExpandValidationNode(ValidationContext context, ModelExplorer modelExplorer)
        {
            var validationNode = context.ValidationNode;

            if (validationNode.ChildNodes.Count != 0 ||
                !validationNode.ValidateAllProperties ||
                validationNode.Model == null)
            {
                return;
            }

            var elementMetadata = modelExplorer.Metadata.ElementMetadata;

            if (elementMetadata == null)
            {
                foreach (var property in validationNode.ModelMetadata.Properties)
                {
                    var propertyExplorer    = modelExplorer.GetExplorerForProperty(property.PropertyName);
                    var propertyBindingName = property.BinderModelName ?? property.PropertyName;
                    var childKey            = ModelNames.CreatePropertyModelName(validationNode.Key, propertyBindingName);
                    var childNode           = new ModelValidationNode(childKey, property, propertyExplorer.Model)
                    {
                        ValidateAllProperties = true
                    };
                    validationNode.ChildNodes.Add(childNode);
                }
            }
            else
            {
                var enumerableModel = (IEnumerable)modelExplorer.Model;

                // An integer index is incorrect in scenarios where there is a custom index provided by the user.
                // However those scenarios are supported by createing a ModelValidationNode with the right keys.
                var index = 0;
                foreach (var element in enumerableModel)
                {
                    var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
                    var elementKey      = ModelNames.CreateIndexModelName(validationNode.Key, index);
                    var childNode       = new ModelValidationNode(elementKey, elementMetadata, elementExplorer.Model)
                    {
                        ValidateAllProperties = true
                    };

                    validationNode.ChildNodes.Add(childNode);
                    index++;
                }
            }
        }
        /// <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),
            });
        }
Beispiel #11
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,
            });
        }
        internal async Task <CollectionResult> BindComplexCollectionFromIndexes(
            IndexModelBindingContext 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);

                var                didBind    = false;
                object             boundValue = null;
                ModelBindingResult?result;
                using (bindingContext.EnterNestedScope(
                           elementMetadata,
                           fieldName: indexName,
                           modelName: fullChildName,
                           model: null))
                {
                    await ElementBinder.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(IndexModelBinderHelper.CastOrDefault <TElement>(boundValue));
            }

            return(new CollectionResult
            {
                Model = boundCollection,

                ValidationStrategy = indexNamesIsFinite ?
                                     new ExplicitIndexCollectionValidationStrategy(indexNames) :
                                     null,
            });
        }
Beispiel #13
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,
            });
        }
    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,
        });
    }