public void GetKeysFromPrefixAsync_ReturnsEmptyDictionaryIfNoValueProviderReturnsValues() { // Arrange var provider1 = Mock.Of<IValueProvider>(); var provider2 = Mock.Of<IValueProvider>(); var provider = new CompositeValueProvider() { provider1, provider2 }; // Act var values = provider.GetKeysFromPrefix("prefix"); // Assert Assert.Empty(values); }
public void FilterReturnsItself_ForAnyClassRegisteredAsGenericParam(IBindingSourceMetadata metadata) { // Arrange var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); var valueProvider1 = GetMockValueProvider("Test"); var valueProvider2 = GetMockValueProvider("Unrelated"); var provider = new CompositeValueProvider() { valueProvider1.Object, valueProvider2.Object }; // Act var result = provider.Filter(metadata.BindingSource); // Assert var valueProvider = Assert.IsType<CompositeValueProvider>(result); var filteredProvider = Assert.Single(valueProvider); // should not be unrelated metadata. Assert.Same(valueProvider1.Object, filteredProvider); }
public void GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues() { // Arrange var provider1 = Mock.Of<IValueProvider>(); var dictionary = new Dictionary<string, string>(StringComparer.Ordinal) { { "prefix-test", "some-value" }, }; var provider2 = new Mock<IEnumerableValueProvider>(); provider2.Setup(p => p.GetKeysFromPrefix("prefix")) .Returns(dictionary) .Verifiable(); var provider = new CompositeValueProvider() { provider1, provider2.Object }; // Act var values = provider.GetKeysFromPrefix("prefix"); // Assert var result = Assert.Single(values); Assert.Equal("prefix-test", result.Key); Assert.Equal("some-value", result.Value); provider2.Verify(); }
private static CompositeValueProvider CreateDefaultValueProvider() { var result = new CompositeValueProvider(); result.Add(new RouteValueProvider(BindingSource.Path, new RouteValueDictionary())); result.Add(new QueryStringValueProvider( BindingSource.Query, new QueryCollection(), CultureInfo.InvariantCulture)); result.Add(new FormValueProvider( BindingSource.Form, new FormCollection(new Dictionary<string, StringValues>()), CultureInfo.CurrentCulture)); return result; }
public void ValueProviderToString_With_CompositeProvider_Formats() { // Arrange List<IValueProvider> providers = new List<IValueProvider>() { new ElementalValueProvider("unused", 1, CultureInfo.CurrentCulture), new NameValueCollectionValueProvider(() => null, CultureInfo.CurrentCulture) }; CompositeValueProvider compositeProvider = new CompositeValueProvider(providers); string expected = String.Format( "{0}({1}, {2})", typeof(CompositeValueProvider).Name, typeof(ElementalValueProvider).Name, typeof(NameValueCollectionValueProvider).Name); // Act string actual = FormattingUtilities.ValueProviderToString(compositeProvider); // Assert Assert.Equal(expected, actual); }
public static ControllerBinderDelegate?CreateBinderDelegate( ParameterBinder parameterBinder, IModelBinderFactory modelBinderFactory, IModelMetadataProvider modelMetadataProvider, ControllerActionDescriptor actionDescriptor, MvcOptions mvcOptions) { if (parameterBinder == null) { throw new ArgumentNullException(nameof(parameterBinder)); } if (modelBinderFactory == null) { throw new ArgumentNullException(nameof(modelBinderFactory)); } if (modelMetadataProvider == null) { throw new ArgumentNullException(nameof(modelMetadataProvider)); } if (actionDescriptor == null) { throw new ArgumentNullException(nameof(actionDescriptor)); } if (mvcOptions == null) { throw new ArgumentNullException(nameof(mvcOptions)); } var parameterBindingInfo = GetParameterBindingInfo( modelBinderFactory, modelMetadataProvider, actionDescriptor); var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor); if (parameterBindingInfo == null && propertyBindingInfo == null) { return(null); } var parameters = actionDescriptor.Parameters switch { List <ParameterDescriptor> list => list.ToArray(), _ => actionDescriptor.Parameters.ToArray() }; var properties = actionDescriptor.BoundProperties switch { List <ParameterDescriptor> list => list.ToArray(), _ => actionDescriptor.BoundProperties.ToArray() }; return(Bind); async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object?> arguments) { var(success, valueProvider) = await CompositeValueProvider.TryCreateAsync(controllerContext, controllerContext.ValueProviderFactories); if (!success) { return; } Debug.Assert(valueProvider is not null); for (var i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; var bindingInfo = parameterBindingInfo ![i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value : null, container : null); // Parameters do not have containers. if (result.IsModelSet) { arguments[parameter.Name] = result.Model; } } for (var i = 0; i < properties.Length; i++) { var property = properties[i]; var bindingInfo = propertyBindingInfo ![i];
public static ControllerBinderDelegate CreateBinderDelegate( ParameterBinder parameterBinder, IModelBinderFactory modelBinderFactory, IModelMetadataProvider modelMetadataProvider, ControllerActionDescriptor actionDescriptor, MvcOptions mvcOptions) { if (parameterBinder == null) { throw new ArgumentNullException(nameof(parameterBinder)); } if (modelBinderFactory == null) { throw new ArgumentNullException(nameof(modelBinderFactory)); } if (modelMetadataProvider == null) { throw new ArgumentNullException(nameof(modelMetadataProvider)); } if (actionDescriptor == null) { throw new ArgumentNullException(nameof(actionDescriptor)); } if (mvcOptions == null) { throw new ArgumentNullException(nameof(mvcOptions)); } var parameterBindingInfo = GetParameterBindingInfo( modelBinderFactory, modelMetadataProvider, actionDescriptor, mvcOptions); var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor); if (parameterBindingInfo == null && propertyBindingInfo == null) { return(null); } return(Bind); async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object> arguments) { var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext); var parameters = actionDescriptor.Parameters; for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value : null); if (result.IsModelSet) { arguments[parameter.Name] = result.Model; } } var properties = actionDescriptor.BoundProperties; for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var bindingInfo = propertyBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, property, modelMetadata, value : null); if (result.IsModelSet) { PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model); } } } }
GetBindingInfo(ActionDescriptor actionDescriptor, RouteContext routeContext) { if (actionDescriptor == null) { throw new ArgumentNullException(nameof(actionDescriptor)); } var bindingResult = (new Dictionary <string, object>(), new Dictionary <ModelMetadata, object>()); var parameterBindingInfo = GetParameterBindingInfo( modelBinderFactory, modelMetadataProvider, actionDescriptor, mvcOptions); BinderItem[] propertyBindingInfo = null; if (actionDescriptor is ControllerActionDescriptor) { propertyBindingInfo = GetPropertyBindingInfo( modelBinderFactory, modelMetadataProvider, actionDescriptor as ControllerActionDescriptor); } if (parameterBindingInfo == null && propertyBindingInfo == null) { return(bindingResult); } //Bind(ControllerContext controllerContext, object controller, Dictionary<string, object> arguments) var actionContext = new ActionContext(routeContext.HttpContext, routeContext.RouteData, actionDescriptor); var controllerContext = new ControllerContext(actionContext) { ValueProviderFactories = new CopyOnWriteList <IValueProviderFactory>(mvcOptions.ValueProviderFactories.ToArray()), }; controllerContext.ModelState.MaxAllowedErrors = mvcOptions.MaxModelValidationErrors; var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext); var parameters = actionDescriptor.Parameters; if (parameterBindingInfo != null) { for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value : null); if (result.IsModelSet) { bindingResult.Item1[parameter.Name] = result.Model; } } } var properties = actionDescriptor.BoundProperties; if (propertyBindingInfo != null && properties != null) { for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var bindingInfo = propertyBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, property, modelMetadata, value : null); if (result.IsModelSet) { //PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model); bindingResult.Item2[bindingInfo.ModelMetadata] = result.Model; } } } return(bindingResult); }
public static PageHandlerBinderDelegate CreateHandlerBinder( ParameterBinder parameterBinder, IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, CompiledPageActionDescriptor actionDescriptor, HandlerMethodDescriptor handler, MvcOptions mvcOptions) { if (handler.Parameters == null || handler.Parameters.Count == 0) { return(NullHandlerBinder); } var handlerType = actionDescriptor.HandlerTypeInfo.AsType(); var parameterBindingInfo = new BinderItem[handler.Parameters.Count]; for (var i = 0; i < parameterBindingInfo.Length; i++) { var parameter = handler.Parameters[i]; ModelMetadata metadata; if (mvcOptions.AllowValidatingTopLevelNodes && modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase) { // The default model metadata provider derives from ModelMetadataProvider // and can therefore supply information about attributes applied to parameters. metadata = modelMetadataProviderBase.GetMetadataForParameter(parameter.ParameterInfo); } else { // For backward compatibility, if there's a custom model metadata provider that // only implements the older IModelMetadataProvider interface, access the more // limited metadata information it supplies. In this scenario, validation attributes // are not supported on parameters. metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType); } var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = parameter.BindingInfo, Metadata = metadata, CacheToken = parameter, }); parameterBindingInfo[i] = new BinderItem(binder, metadata); } return(Bind); async Task Bind(PageContext pageContext, IDictionary <string, object> arguments) { var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories); for (var i = 0; i < parameterBindingInfo.Length; i++) { var parameter = handler.Parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( pageContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value : null); if (result.IsModelSet) { arguments[parameter.Name] = result.Model; } } } }
public static Func <PageContext, object, Task> CreatePropertyBinder( ParameterBinder parameterBinder, IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, CompiledPageActionDescriptor actionDescriptor) { if (parameterBinder == null) { throw new ArgumentNullException(nameof(parameterBinder)); } if (actionDescriptor == null) { throw new ArgumentNullException(nameof(actionDescriptor)); } var properties = actionDescriptor.BoundProperties; if (properties == null || properties.Count == 0) { return(NullPropertyBinder); } var handlerType = actionDescriptor.HandlerTypeInfo.AsType(); var propertyBindingInfo = new BinderItem[properties.Count]; for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var metadata = modelMetadataProvider.GetMetadataForProperty(handlerType, property.Name); var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = property.BindingInfo, Metadata = metadata, CacheToken = property, }); propertyBindingInfo[i] = new BinderItem(binder, metadata); } return(Bind); async Task Bind(PageContext pageContext, object instance) { var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories); for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var bindingInfo = propertyBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( pageContext, bindingInfo.ModelBinder, valueProvider, property, modelMetadata, value : null); if (result.IsModelSet) { PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, instance, result.Model); } } } }
public JObject ParseProperties(string modelName, CompositeValueProvider valueProvider, JObject model) { IDictionary <string, string> properties = valueProvider.GetKeysFromPrefix(modelName); _logger.LogDebug("DynamicModelBinder property count is " + properties.Count() + " for " + modelName); List <string> subModelNames = new List <string>(); List <string> arrModelNames = new List <string>(); foreach (var property in properties) { var subProperties = valueProvider.GetKeysFromPrefix(property.Value); var key = property.Value; var propName = property.Key; if (subProperties.Count == 0) { if (!propName.Contains("RequestVerification")) { if (!model.ContainsKey(propName)) { model.Add(propName, GetValue(valueProvider, key)); } else { model[propName] = GetValue(valueProvider, key); } } } else if (subProperties.Any(sp => sp.Value.Contains("["))) { if (!arrModelNames.Contains(propName)) { arrModelNames.Add(propName); } } else { if (!subModelNames.Contains(propName)) { subModelNames.Add(propName); } } } foreach (var subModelName in subModelNames) { var key = properties[subModelName]; JObject val = ParseProperties(key, valueProvider, model.ContainsKey(subModelName) ? (JObject)model[subModelName] : new JObject()); if (!model.ContainsKey(subModelName)) { model.Add(subModelName, val); } else { model[subModelName] = val; } } foreach (var arrModelName in arrModelNames) { var key = properties[arrModelName]; var arrKeys = valueProvider.GetKeysFromPrefix(key); var isComplexArray = false; foreach (var arrKey in arrKeys) { var subProperties = valueProvider.GetKeysFromPrefix(arrKey.Value); if (subProperties.Count > 0) { isComplexArray = true; } } JToken arrResult = null; List <object> vals = new List <object>(); vals.Cast <Object>().ToList(); if (isComplexArray) { foreach (var arrKey in arrKeys) { var arrItemKey = arrKey.Value; var subProperties = valueProvider.GetKeysFromPrefix(arrItemKey); if (subProperties.Count > 0) { object val = ParseProperties(arrItemKey, valueProvider, new JObject()); vals.Add(val); } } arrResult = new JArray(vals); } else { foreach (var arrKey in arrKeys) { var arrItemKey = arrKey.Value; vals.Add(GetValue(valueProvider, arrItemKey)); } arrResult = new JArray(vals); bool castToType = true; Type itemType = vals[0].GetType(); foreach (var item in vals) { if (item.GetType() != itemType) { castToType = false; break; } } if (castToType) { var ienumerable = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(itemType).Invoke(null, new object[] { vals }); arrResult = new JArray(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(itemType).Invoke(null, new object[] { ienumerable })); } } if (!model.ContainsKey(arrModelName)) { model.Add(arrModelName, arrResult); } else { model[arrModelName] = arrResult; } } return(model); }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var modelName = bindingContext.ModelName; var valueProvider = await CompositeValueProvider.CreateAsync(bindingContext.ActionContext, new CopyOnWriteList <IValueProviderFactory>(_valueProviderFactories)); JObject model = bindingContext.Model != null ? (JObject)bindingContext.Model : new JObject(); //Form/Route/Query if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Form) || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Query) || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Path)) { model = ParseProperties(modelName, valueProvider, model); } //Route if (bindingContext.IsTopLevelObject) { if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Path)) { foreach (var kvp in bindingContext.ActionContext.RouteData.Values) { if (kvp.Key != "area" && kvp.Key != "controller" && kvp.Key != "action" && !model.ContainsKey(kvp.Key)) { var stringValue = kvp.Value as string ?? Convert.ToString(kvp.Value, CultureInfo.InvariantCulture) ?? string.Empty; if (!model.ContainsKey(kvp.Key)) { model.Add(kvp.Key, GetValue(stringValue)); } else if (bindingContext.HttpContext.Request.Query.ContainsKey(kvp.Key) && !bindingContext.HttpContext.Request.Form.ContainsKey(kvp.Key)) { model[kvp.Key] = GetValue(stringValue); } } } } } //Query if (bindingContext.IsTopLevelObject) { if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Query)) { foreach (var kvp in bindingContext.HttpContext.Request.Query) { if (!model.ContainsKey(kvp.Key)) { model.Add(kvp.Key, GetValue(kvp.Value)); } else if (!bindingContext.HttpContext.Request.Form.ContainsKey(kvp.Key) && !bindingContext.ActionContext.RouteData.Values.ContainsKey(kvp.Key)) { model[kvp.Key] = GetValue(kvp.Value); } } } } bindingContext.Result = ModelBindingResult.Success(model); }