diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs
index b9eb3862d0..db1706d964 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs
@@ -1,10 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
using System.Collections.Generic;
using System.Globalization;
-using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
@@ -12,11 +10,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
/// An for jQuery formatted form data.
///
- public class JQueryFormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
+ public class JQueryFormValueProvider : JQueryValueProvider
{
- private readonly IDictionary _values;
- private PrefixContainer _prefixContainer;
-
///
/// Initializes a new instance of the class.
///
@@ -27,60 +22,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
BindingSource bindingSource,
IDictionary values,
CultureInfo culture)
- : base(bindingSource)
+ : base(bindingSource, values, culture)
{
- if (bindingSource == null)
- {
- throw new ArgumentNullException(nameof(bindingSource));
- }
-
- if (values == null)
- {
- throw new ArgumentNullException(nameof(values));
- }
-
- _values = values;
- Culture = culture;
- }
-
- // Internal for testing
- internal CultureInfo Culture { get; }
-
- protected PrefixContainer PrefixContainer
- {
- get
- {
- if (_prefixContainer == null)
- {
- _prefixContainer = new PrefixContainer(_values.Keys);
- }
-
- return _prefixContainer;
- }
- }
-
- ///
- public override bool ContainsPrefix(string prefix)
- {
- return PrefixContainer.ContainsPrefix(prefix);
- }
-
- ///
- public IDictionary GetKeysFromPrefix(string prefix)
- {
- return PrefixContainer.GetKeysFromPrefix(prefix);
- }
-
- ///
- public override ValueProviderResult GetValue(string key)
- {
- StringValues values;
- if (_values.TryGetValue(key, out values) && values.Count > 0)
- {
- return new ValueProviderResult(values, Culture);
- }
-
- return ValueProviderResult.None;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs
index e007ea3160..fdd8eb2d93 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs
@@ -2,18 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.Globalization;
-using System.Text;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc.Core;
-using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
///
- /// A for .
+ /// An for .
///
public class JQueryFormValueProviderFactory : IValueProviderFactory
{
@@ -38,97 +33,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private static async Task AddValueProviderAsync(ValueProviderFactoryContext context)
{
var request = context.ActionContext.HttpContext.Request;
+
+ var formCollection = await request.ReadFormAsync();
+
var valueProvider = new JQueryFormValueProvider(
BindingSource.Form,
- await GetValueCollectionAsync(request),
+ JQueryKeyValuePairNormalizer.GetValues(formCollection, formCollection.Count),
CultureInfo.CurrentCulture);
context.ValueProviders.Add(valueProvider);
}
-
- private static async Task> GetValueCollectionAsync(HttpRequest request)
- {
- var formCollection = await request.ReadFormAsync();
-
- var builder = new StringBuilder();
- var dictionary = new Dictionary(
- formCollection.Count,
- StringComparer.OrdinalIgnoreCase);
- foreach (var entry in formCollection)
- {
- var key = NormalizeJQueryToMvc(builder, entry.Key);
- builder.Clear();
-
- dictionary[key] = entry.Value;
- }
-
- return dictionary;
- }
-
- // This is a helper method for Model Binding over a JQuery syntax.
- // Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys.
- // x[] --> x
- // [] --> ""
- // x[12] --> x[12]
- // x[field] --> x.field, where field is not a number
- private static string NormalizeJQueryToMvc(StringBuilder builder, string key)
- {
- if (string.IsNullOrEmpty(key))
- {
- return string.Empty;
- }
-
- var indexOpen = key.IndexOf('[');
- if (indexOpen == -1)
- {
-
- // Fast path, no normalization needed.
- // This skips string conversion and allocating the string builder.
- return key;
- }
-
- var position = 0;
- while (position < key.Length)
- {
- if (indexOpen == -1)
- {
- // No more brackets.
- builder.Append(key, position, key.Length - position);
- break;
- }
-
- builder.Append(key, position, indexOpen - position); // everything up to "["
-
- // Find closing bracket.
- var indexClose = key.IndexOf(']', indexOpen);
- if (indexClose == -1)
- {
- throw new ArgumentException(
- message: Resources.FormatJQueryFormValueProviderFactory_MissingClosingBracket(key),
- paramName: nameof(key));
- }
-
- if (indexClose == indexOpen + 1)
- {
- // Empty brackets signify an array. Just remove.
- }
- else if (char.IsDigit(key[indexOpen + 1]))
- {
- // Array index. Leave unchanged.
- builder.Append(key, indexOpen, indexClose - indexOpen + 1);
- }
- else
- {
- // Field name. Convert to dot notation.
- builder.Append('.');
- builder.Append(key, indexOpen + 1, indexClose - indexOpen - 1);
- }
-
- position = indexClose + 1;
- indexOpen = key.IndexOf('[', position);
- }
-
- return builder.ToString();
- }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryKeyValuePairNormalizer.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryKeyValuePairNormalizer.cs
new file mode 100644
index 0000000000..b9fcc2520f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryKeyValuePairNormalizer.cs
@@ -0,0 +1,100 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ // Normalizes keys, in a keyvaluepair collection, from jQuery format to a format that MVC understands.
+ internal static class JQueryKeyValuePairNormalizer
+ {
+ public static IDictionary GetValues(
+ IEnumerable> originalValues,
+ int valueCount)
+ {
+ var builder = new StringBuilder();
+ var dictionary = new Dictionary(
+ valueCount,
+ StringComparer.OrdinalIgnoreCase);
+ foreach (var originalValue in originalValues)
+ {
+ var normalizedKey = NormalizeJQueryToMvc(builder, originalValue.Key);
+ builder.Clear();
+
+ dictionary[normalizedKey] = originalValue.Value;
+ }
+
+ return dictionary;
+ }
+
+ // This is a helper method for Model Binding over a JQuery syntax.
+ // Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys.
+ // x[] --> x
+ // [] --> ""
+ // x[12] --> x[12]
+ // x[field] --> x.field, where field is not a number
+ private static string NormalizeJQueryToMvc(StringBuilder builder, string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ return string.Empty;
+ }
+
+ var indexOpen = key.IndexOf('[');
+ if (indexOpen == -1)
+ {
+
+ // Fast path, no normalization needed.
+ // This skips string conversion and allocating the string builder.
+ return key;
+ }
+
+ var position = 0;
+ while (position < key.Length)
+ {
+ if (indexOpen == -1)
+ {
+ // No more brackets.
+ builder.Append(key, position, key.Length - position);
+ break;
+ }
+
+ builder.Append(key, position, indexOpen - position); // everything up to "["
+
+ // Find closing bracket.
+ var indexClose = key.IndexOf(']', indexOpen);
+ if (indexClose == -1)
+ {
+ throw new ArgumentException(
+ message: Resources.FormatJQueryFormValueProviderFactory_MissingClosingBracket(key),
+ paramName: nameof(key));
+ }
+
+ if (indexClose == indexOpen + 1)
+ {
+ // Empty brackets signify an array. Just remove.
+ }
+ else if (char.IsDigit(key[indexOpen + 1]))
+ {
+ // Array index. Leave unchanged.
+ builder.Append(key, indexOpen, indexClose - indexOpen + 1);
+ }
+ else
+ {
+ // Field name. Convert to dot notation.
+ builder.Append('.');
+ builder.Append(key, indexOpen + 1, indexClose - indexOpen - 1);
+ }
+
+ position = indexClose + 1;
+ indexOpen = key.IndexOf('[', position);
+ }
+
+ return builder.ToString();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProvider.cs
new file mode 100644
index 0000000000..4d50ab8b88
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProvider.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Globalization;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ ///
+ /// An for jQuery formatted query string data.
+ ///
+ public class JQueryQueryStringValueProvider : JQueryValueProvider
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The of the data.
+ /// The values.
+ /// The culture to return with ValueProviderResult instances.
+ public JQueryQueryStringValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
+ : base(bindingSource, values, culture)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProviderFactory.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProviderFactory.cs
new file mode 100644
index 0000000000..f454bb6216
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryQueryStringValueProviderFactory.cs
@@ -0,0 +1,37 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ ///
+ /// An for .
+ ///
+ public class JQueryQueryStringValueProviderFactory : IValueProviderFactory
+ {
+ ///
+ public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var query = context.ActionContext.HttpContext.Request.Query;
+ if (query != null && query.Count > 0)
+ {
+ var valueProvider = new JQueryQueryStringValueProvider(
+ BindingSource.Query,
+ JQueryKeyValuePairNormalizer.GetValues(query, query.Count),
+ CultureInfo.InvariantCulture);
+
+ context.ValueProviders.Add(valueProvider);
+ }
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryValueProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryValueProvider.cs
new file mode 100644
index 0000000000..a2ffca0f2a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/JQueryValueProvider.cs
@@ -0,0 +1,89 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ ///
+ /// An for jQuery formatted form data.
+ ///
+ public abstract class JQueryValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
+ {
+ private readonly IDictionary _values;
+ private PrefixContainer _prefixContainer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The of the data.
+ /// The values.
+ /// The culture to return with ValueProviderResult instances.
+ protected JQueryValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
+ : base(bindingSource)
+ {
+ if (bindingSource == null)
+ {
+ throw new ArgumentNullException(nameof(bindingSource));
+ }
+
+ if (values == null)
+ {
+ throw new ArgumentNullException(nameof(values));
+ }
+
+ _values = values;
+ Culture = culture;
+ }
+
+ ///
+ /// Gets the associated with the values.
+ ///
+ public CultureInfo Culture { get; }
+
+ ///
+ protected PrefixContainer PrefixContainer
+ {
+ get
+ {
+ if (_prefixContainer == null)
+ {
+ _prefixContainer = new PrefixContainer(_values.Keys);
+ }
+
+ return _prefixContainer;
+ }
+ }
+
+ ///
+ public override bool ContainsPrefix(string prefix)
+ {
+ return PrefixContainer.ContainsPrefix(prefix);
+ }
+
+ ///
+ public IDictionary GetKeysFromPrefix(string prefix)
+ {
+ return PrefixContainer.GetKeysFromPrefix(prefix);
+ }
+
+ ///
+ public override ValueProviderResult GetValue(string key)
+ {
+ StringValues values;
+ if (_values.TryGetValue(key, out values) && values.Count > 0)
+ {
+ return new ValueProviderResult(values, Culture);
+ }
+
+ return ValueProviderResult.None;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs
index 435963bee5..b6e620df82 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs
@@ -21,12 +21,16 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
throw new ArgumentNullException(nameof(context));
}
- var valueProvider = new QueryStringValueProvider(
- BindingSource.Query,
- context.ActionContext.HttpContext.Request.Query,
- CultureInfo.InvariantCulture);
+ var query = context.ActionContext.HttpContext.Request.Query;
+ if (query != null && query.Count > 0)
+ {
+ var valueProvider = new QueryStringValueProvider(
+ BindingSource.Query,
+ query,
+ CultureInfo.InvariantCulture);
- context.ValueProviders.Add(valueProvider);
+ context.ValueProviders.Add(valueProvider);
+ }
return Task.CompletedTask;
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Mvc.Core/breakingchanges.netcore.json
index 07cf4ab0af..9546877aea 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/breakingchanges.netcore.json
+++ b/src/Microsoft.AspNetCore.Mvc.Core/breakingchanges.netcore.json
@@ -28,5 +28,9 @@
"TypeId": "public class Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinderProvider : Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinderProvider",
"MemberId": "public .ctor(System.Collections.Generic.IList formatters, Microsoft.AspNetCore.Mvc.Internal.IHttpRequestStreamReaderFactory readerFactory, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Mvc.MvcOptions options)",
"Kind": "Removal"
+ },
+ {
+ "TypeId": "public class Microsoft.AspNetCore.Mvc.ModelBinding.JQueryFormValueProvider : Microsoft.AspNetCore.Mvc.ModelBinding.BindingSourceValueProvider, Microsoft.AspNetCore.Mvc.ModelBinding.IEnumerableValueProvider",
+ "Kind": "Removal"
}
]
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs
index 92049bfa83..b968a92f19 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs
@@ -116,6 +116,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test
Assert.Equal("found", (string)result);
}
+ [Fact]
+ public async Task CreatesValueProvider_WithCurrentCulture()
+ {
+ // Arrange
+ var context = CreateContext("application/x-www-form-urlencoded", formValues: _backingStore);
+ var factory = new JQueryFormValueProviderFactory();
+
+ // Act
+ await factory.CreateValueProviderAsync(context);
+
+ // Assert
+ var valueProvider = Assert.Single(context.ValueProviders);
+ var jqueryFormValueProvider = Assert.IsType(valueProvider);
+ Assert.Equal(CultureInfo.CurrentCulture, jqueryFormValueProvider.Culture);
+ }
+
private static ValueProviderFactoryContext CreateContext(string contentType, Dictionary formValues)
{
var context = new DefaultHttpContext();
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs
new file mode 100644
index 0000000000..a3133261c3
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs
@@ -0,0 +1,127 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Internal;
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Primitives;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test
+{
+ public class JQueryQueryStringValueProviderFactoryTest
+ {
+ private static readonly Dictionary _backingStore = new Dictionary
+ {
+ { "[]", new[] { "found" } },
+ { "[]property1", new[] { "found" } },
+ { "property2[]", new[] { "found" } },
+ { "[]property3[]", new[] { "found" } },
+ { "property[]Value", new[] { "found" } },
+ { "[10]", new[] { "found" } },
+ { "[11]property", new[] { "found" } },
+ { "property4[10]", new[] { "found" } },
+ { "[12]property[][13]", new[] { "found" } },
+ { "[14][]property1[15]property2", new[] { "found" } },
+ { "prefix[11]property1", new[] { "found" } },
+ { "prefix[12][][property2]", new[] { "found" } },
+ { "prefix[property1][13]", new[] { "found" } },
+ { "prefix[14][][15]", new[] { "found" } },
+ { "[property5][]", new[] { "found" } },
+ { "[][property6]Value", new[] { "found" } },
+ { "prefix[property2]", new[] { "found" } },
+ { "prefix[][property]Value", new[] { "found" } },
+ { "[property7][property8]", new[] { "found" } },
+ { "[property9][][property10]Value", new[] { "found" } },
+ };
+
+ public static TheoryData SuccessDataSet
+ {
+ get
+ {
+ return new TheoryData
+ {
+ string.Empty,
+ "property1",
+ "property2",
+ "property3",
+ "propertyValue",
+ "[10]",
+ "[11]property",
+ "property4[10]",
+ "[12]property[13]",
+ "[14]property1[15]property2",
+ "prefix.property1[13]",
+ "prefix[14][15]",
+ ".property5",
+ ".property6Value",
+ "prefix.property2",
+ "prefix.propertyValue",
+ ".property7.property8",
+ ".property9.property10Value",
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(SuccessDataSet))]
+ public async Task GetValueProvider_ReturnsValueProvider_ContainingExpectedKeys(string key)
+ {
+ // Arrange
+ var context = CreateContext(_backingStore);
+ var factory = new JQueryQueryStringValueProviderFactory();
+
+ // Act
+ await factory.CreateValueProviderAsync(context);
+
+ // Assert
+ var valueProvider = Assert.Single(context.ValueProviders);
+ var result = valueProvider.GetValue(key);
+ Assert.Equal("found", (string)result);
+ }
+
+ [Fact]
+ public async Task DoesNotCreateValueProvider_WhenQueryIsEmpty()
+ {
+ // Arrange
+ var context = CreateContext(new Dictionary());
+ var factory = new JQueryQueryStringValueProviderFactory();
+
+ // Act
+ await factory.CreateValueProviderAsync(context);
+
+ // Assert
+ Assert.Empty(context.ValueProviders);
+ }
+
+ [Fact]
+ public async Task CreatesValueProvider_WithInvariantCulture()
+ {
+ // Arrange
+ var context = CreateContext(_backingStore);
+ var factory = new JQueryQueryStringValueProviderFactory();
+
+ // Act
+ await factory.CreateValueProviderAsync(context);
+
+ // Assert
+ var valueProvider = Assert.Single(context.ValueProviders);
+ var jqueryQueryStringValueProvider = Assert.IsType(valueProvider);
+ Assert.Equal(CultureInfo.InvariantCulture, jqueryQueryStringValueProvider.Culture);
+ }
+
+ private static ValueProviderFactoryContext CreateContext(Dictionary queryStringValues)
+ {
+ var context = new DefaultHttpContext();
+
+ context.Request.Query = new QueryCollection(queryStringValues);
+
+ var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());
+ return new ValueProviderFactoryContext(actionContext);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderTest.cs
new file mode 100644
index 0000000000..cb8453fd6b
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderTest.cs
@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Globalization;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ public class JQueryQueryStringValueProviderTest : EnumerableValueProviderTest
+ {
+ protected override IEnumerableValueProvider GetEnumerableValueProvider(
+ BindingSource bindingSource,
+ Dictionary values,
+ CultureInfo culture)
+ {
+ return new JQueryQueryStringValueProvider(bindingSource, values, culture);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs
index 092bc5c08d..40dbfb0d3c 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs
@@ -5,25 +5,40 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Routing;
-using Moq;
+using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test
{
public class QueryStringValueProviderFactoryTest
{
+ [Fact]
+ public async Task DoesNotCreateValueProvider_WhenQueryStringIsEmpty()
+ {
+ // Arrange
+ var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
+ var factoryContext = new ValueProviderFactoryContext(actionContext);
+ var factory = new QueryStringValueProviderFactory();
+
+ // Act
+ await factory.CreateValueProviderAsync(factoryContext);
+
+ // Assert
+ Assert.Empty(factoryContext.ValueProviders);
+ }
+
[Fact]
public async Task GetValueProvider_ReturnsQueryStringValueProviderInstanceWithInvariantCulture()
{
// Arrange
- var request = new Mock();
- request.SetupGet(f => f.Query).Returns(Mock.Of());
- var context = new Mock();
- context.SetupGet(c => c.Items).Returns(new Dictionary