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); }
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)); }
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); } } }
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)); }
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); }
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("&"); } }
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); }
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), }); }
// 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¶meter.index=one¶meter.index=two¶meter[zero]=0¶meter[one]=1¶meter[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, }); }
// 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¶meter[zero]=0&¶meter[one]=1¶meter[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¶meter.index=one¶meter.index=two¶meter[zero]=0¶meter[one]=1¶meter[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, }); }