diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs
index 78a2b691df..7508513b57 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs
@@ -14,8 +14,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// Represents a whose values come from a collection of s.
///
public class CompositeValueProvider :
- Collection,
- IEnumerableValueProvider,
+ Collection,
+ IEnumerableValueProvider,
IBindingSourceValueProvider
{
///
@@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// created.
///
public static CompositeValueProvider Create(
- [NotNull] IEnumerable factories,
+ [NotNull] IEnumerable factories,
[NotNull] ValueProviderFactoryContext context)
{
var composite = new CompositeValueProvider();
@@ -137,6 +137,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
+ if (filteredValueProviders.Count == Count)
+ {
+ // No need for a new CompositeValueProvider.
+ return this;
+ }
+
return new CompositeValueProvider(filteredValueProviders);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/IBindingSourceValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/IBindingSourceValueProvider.cs
index 1eb69d687d..752f0fc2e7 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/IBindingSourceValueProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/IBindingSourceValueProvider.cs
@@ -6,7 +6,7 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
///
- /// A value provider which is which can filter its contents based on .
+ /// A value provider which can filter its contents based on .
///
///
/// Value providers are by-default included. If a model does not specify a
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs
new file mode 100644
index 0000000000..e1e319dd95
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs
@@ -0,0 +1,114 @@
+// 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.Diagnostics;
+using System.Globalization;
+using System.Threading.Tasks;
+using Microsoft.Framework.Internal;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ ///
+ /// An for form data stored in an and
+ /// generally accessed asynchronously.
+ ///
+ public class JQueryFormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
+ {
+ private readonly Func>> _valuesFactory;
+
+ private PrefixContainer _prefixContainer;
+ private IDictionary _values;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The of the data.
+ /// A delegate which provides the values to wrap.
+ /// The culture to return with ValueProviderResult instances.
+ public JQueryFormValueProvider(
+ [NotNull] BindingSource bindingSource,
+ [NotNull] Func>> valuesFactory,
+ CultureInfo culture)
+ : base(bindingSource)
+ {
+ _valuesFactory = valuesFactory;
+ Culture = culture;
+ }
+
+ // Internal for testing.
+ internal JQueryFormValueProvider(
+ [NotNull] BindingSource bindingSource,
+ [NotNull] IDictionary values,
+ CultureInfo culture)
+ : base(bindingSource)
+ {
+ _values = values;
+ Culture = culture;
+ }
+
+ // Internal for testing
+ internal CultureInfo Culture { get; }
+
+ ///
+ public override async Task ContainsPrefixAsync(string prefix)
+ {
+ var prefixContainer = await GetPrefixContainerAsync();
+ return prefixContainer.ContainsPrefix(prefix);
+ }
+
+ ///
+ public async Task> GetKeysFromPrefixAsync(string prefix)
+ {
+ var prefixContainer = await GetPrefixContainerAsync();
+ return prefixContainer.GetKeysFromPrefix(prefix);
+ }
+
+ ///
+ public override async Task GetValueAsync(string key)
+ {
+ var dictionary = await GetDictionary();
+
+ string[] values;
+ if (dictionary.TryGetValue(key, out values) && values != null && values.Length > 0)
+ {
+ // Success.
+ if (values.Length == 1)
+ {
+ return new ValueProviderResult(values[0], values[0], Culture);
+ }
+
+ return new ValueProviderResult(values, string.Join(",", values), Culture);
+ }
+
+ return null;
+ }
+
+ private async Task> GetDictionary()
+ {
+ if (_values == null)
+ {
+ Debug.Assert(_valuesFactory != null);
+
+ // Initialization race is OK providing data remains read-only.
+ _values = await _valuesFactory();
+ }
+
+ return _values;
+ }
+
+ private async Task GetPrefixContainerAsync()
+ {
+ if (_prefixContainer == null)
+ {
+ var dictionary = await GetDictionary();
+
+ // Initialization race is OK providing data remains read-only and object identity is not significant.
+ _prefixContainer = new PrefixContainer(dictionary.Keys);
+ }
+
+ return _prefixContainer;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs
new file mode 100644
index 0000000000..e40cd205c0
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProviderFactory.cs
@@ -0,0 +1,113 @@
+// 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 System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Mvc.Core;
+using Microsoft.Framework.Internal;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ public class JQueryFormValueProviderFactory : IValueProviderFactory
+ {
+ public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
+ {
+ var request = context.HttpContext.Request;
+
+ if (request.HasFormContentType)
+ {
+ return new JQueryFormValueProvider(
+ BindingSource.Form,
+ () => GetValueCollectionAsync(request),
+ CultureInfo.CurrentCulture);
+ }
+
+ return null;
+ }
+
+ private static async Task> GetValueCollectionAsync(HttpRequest request)
+ {
+ var formCollection = await request.ReadFormAsync();
+
+ var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var entry in formCollection)
+ {
+ var key = NormalizeJQueryToMvc(entry.Key);
+ 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(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 builder = new StringBuilder();
+ 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: "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.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs
index ae68ccf14c..5d92d989c8 100644
--- a/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs
@@ -47,6 +47,7 @@ namespace Microsoft.AspNet.Mvc
options.ValueProviderFactories.Add(new RouteValueValueProviderFactory());
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
options.ValueProviderFactories.Add(new FormValueProviderFactory());
+ options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
// Set up metadata providers
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index ca9ccc6dd3..ef35c6b217 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -1034,6 +1034,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
}
+ ///
+ /// The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.
+ ///
+ internal static string JQueryFormValueProviderFactory_MissingClosingBracket
+ {
+ get { return GetString("JQueryFormValueProviderFactory_MissingClosingBracket"); }
+ }
+
+ ///
+ /// The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.
+ ///
+ internal static string FormatJQueryFormValueProviderFactory_MissingClosingBracket(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("JQueryFormValueProviderFactory_MissingClosingBracket"), p0);
+ }
+
///
/// A value is required.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index bb5aa0eaa8..7ba416e14c 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -1,17 +1,17 @@
-
@@ -318,6 +318,9 @@
The property {0}.{1} could not be found.
+
+ The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.
+
A value is required.
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs
similarity index 80%
rename from test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTests.cs
rename to test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs
index 6af057aa15..c341a04f9f 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs
@@ -2,17 +2,41 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
-
using System;
+#endif
using System.Collections.Generic;
+using System.Globalization;
+#if DNX451
using System.Threading.Tasks;
using Moq;
using Xunit;
+#endif
namespace Microsoft.AspNet.Mvc.ModelBinding
{
- public class CompositeValueProviderTests
+ public class CompositeValueProviderTest : EnumerableValueProviderTest
{
+ protected override IEnumerableValueProvider GetEnumerableValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
+ {
+ var emptyValueProvider =
+ new JQueryFormValueProvider(bindingSource, new Dictionary(), culture);
+ var valueProvider = new JQueryFormValueProvider(bindingSource, values, culture);
+
+ return new CompositeValueProvider(new[] { emptyValueProvider, valueProvider });
+ }
+
+ protected override void CheckFilterExcludeResult(IValueProvider result)
+ {
+ // CompositeValueProvider returns an empty instance rather than null. CompositeModelBinder and
+ // MutableObjectModelBinder depend on this empty instance.
+ var compositeProvider = Assert.IsType(result);
+ Assert.Empty(compositeProvider);
+ }
+
+#if DNX451
[Fact]
public async Task GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues()
{
@@ -125,6 +149,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
}
+#endif
}
}
-#endif
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs
new file mode 100644
index 0000000000..6cd11dd52f
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs
@@ -0,0 +1,302 @@
+// 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.Linq;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ public abstract class EnumerableValueProviderTest
+ {
+ private static readonly IDictionary _backingStore = new Dictionary
+ {
+ { "some", new[] { "someValue1", "someValue2" } },
+ { "null_value", null },
+ { "prefix.name", new[] { "someOtherValue" } },
+ { "prefix.null_value", null },
+ { "prefix.property1.property", null },
+ { "prefix.property2[index]", null },
+ { "prefix[index1]", null },
+ { "prefix[index1].property1", null },
+ { "prefix[index1].property2", null },
+ { "prefix[index2].property", null },
+ { "[index]", null },
+ { "[index].property", null },
+ { "[index][anotherIndex]", null },
+ };
+
+ [Fact]
+ public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
+ {
+ // Arrange
+ var backingStore = new Dictionary();
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.ContainsPrefixAsync(string.Empty);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
+ {
+ // Arrange
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.ContainsPrefixAsync(string.Empty);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
+ {
+ // Arrange
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act & Assert
+ Assert.True(await valueProvider.ContainsPrefixAsync("some"));
+ Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
+ Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
+ Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
+ Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
+ }
+
+ [Fact]
+ public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
+ {
+ // Arrange
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.ContainsPrefixAsync("biff");
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
+ {
+ // Arrange
+ var expected = new Dictionary
+ {
+ { "index", "[index]" },
+ { "null_value", "null_value" },
+ { "prefix", "prefix" },
+ { "some", "some" },
+ };
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty);
+
+ // Assert
+ Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
+ }
+
+ [Fact]
+ public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
+ {
+ // Arrange
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.GetKeysFromPrefixAsync("abc");
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
+ {
+ // Arrange
+ var expected = new Dictionary
+ {
+ { "name", "prefix.name" },
+ { "null_value", "prefix.null_value" },
+ { "property1", "prefix.property1" },
+ { "property2", "prefix.property2" },
+ { "index1", "prefix[index1]" },
+ { "index2", "prefix[index2]" },
+ };
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.GetKeysFromPrefixAsync("prefix");
+
+ // Assert
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems()
+ {
+ // Arrange
+ var expected = new Dictionary
+ {
+ { "property", "[index].property" },
+ { "anotherIndex", "[index][anotherIndex]" }
+ };
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.GetKeysFromPrefixAsync("[index]");
+
+ // Assert
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public async Task GetValueAsync_SingleValue()
+ {
+ // Arrange
+ var culture = new CultureInfo("fr-FR");
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
+
+ // Act
+ var result = await valueProvider.GetValueAsync("prefix.name");
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal("someOtherValue", result.RawValue);
+ Assert.Equal("someOtherValue", result.AttemptedValue);
+ Assert.Equal(culture, result.Culture);
+ }
+
+ [Fact]
+ public async Task GetValueAsync_MultiValue()
+ {
+ // Arrange
+ var culture = new CultureInfo("fr-FR");
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
+
+ // Act
+ var result = await valueProvider.GetValueAsync("some");
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(new[] { "someValue1", "someValue2" }, (IList)result.RawValue);
+ Assert.Equal("someValue1,someValue2", result.AttemptedValue);
+ Assert.Equal(culture, result.Culture);
+ }
+
+ [Theory]
+ [InlineData("null_value")]
+ [InlineData("prefix.null_value")]
+ public async Task GetValue_NullValue(string key)
+ {
+ // Arrange
+ var culture = new CultureInfo("fr-FR");
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
+
+ // Act
+ var result = await valueProvider.GetValueAsync(key);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public async Task GetValueAsync_NullMultipleValue()
+ {
+ // Arrange
+ var backingStore = new Dictionary
+ {
+ { "key", new string[] { null, null, "value" } },
+ };
+ var culture = new CultureInfo("fr-FR");
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture);
+
+ // Act
+ var result = await valueProvider.GetValueAsync("key");
+
+ // Assert
+ Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable);
+ Assert.Equal(",,value", result.AttemptedValue);
+ }
+
+ [Fact]
+ public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
+ {
+ // Arrange
+ var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ // Act
+ var result = await valueProvider.GetValueAsync("prefix");
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void FilterInclude()
+ {
+ // Arrange
+ var provider = GetBindingSourceValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ var bindingSource = new BindingSource(
+ BindingSource.Query.Id,
+ displayName: null,
+ isGreedy: true,
+ isFromRequest: true);
+
+ // Act
+ var result = provider.Filter(bindingSource);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Same(result, provider);
+ }
+
+ [Fact]
+ public void FilterExclude()
+ {
+ // Arrange
+ var provider = GetBindingSourceValueProvider(BindingSource.Query, _backingStore, culture: null);
+
+ var bindingSource = new BindingSource(
+ "Test",
+ displayName: null,
+ isGreedy: true,
+ isFromRequest: true);
+
+ // Act
+ var result = provider.Filter(bindingSource);
+
+ // Assert
+ CheckFilterExcludeResult(result);
+ }
+
+ protected virtual void CheckFilterExcludeResult(IValueProvider result)
+ {
+ Assert.Null(result);
+ }
+
+ private IBindingSourceValueProvider GetBindingSourceValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
+ {
+ var provider = GetEnumerableValueProvider(bindingSource, values, culture) as IBindingSourceValueProvider;
+
+ // All IEnumerableValueProvider implementations also implement IBindingSourceValueProvider.
+ Assert.NotNull(provider);
+
+ return provider;
+ }
+
+ protected abstract IEnumerableValueProvider GetEnumerableValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture);
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs
similarity index 69%
rename from test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTests.cs
rename to test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs
index de41590e31..5ef29bcea4 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs
@@ -1,20 +1,15 @@
// 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.
-#if DNX451
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNet.Http;
-using Microsoft.AspNet.Http.Features.Internal;
-using Moq;
+using Microsoft.AspNet.Http.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
- public class FormValueProviderFactoryTests
+ public class FormValueProviderFactoryTest
{
[Fact]
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
@@ -35,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[InlineData("application/x-www-form-urlencoded;charset=utf-8")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
- public void GetValueProvider_ReturnsValueProviderInstanceWithInvariantCulture(string contentType)
+ public void GetValueProvider_ReturnsValueProviderInstanceWithCurrentCulture(string contentType)
{
// Arrange
var context = CreateContext(contentType);
@@ -51,19 +46,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
private static ValueProviderFactoryContext CreateContext(string contentType)
{
- var collection = Mock.Of();
- var request = new Mock();
- request.Setup(f => f.ReadFormAsync(CancellationToken.None)).Returns(Task.FromResult(collection));
- request.SetupGet(r => r.ContentType).Returns(contentType);
- request.SetupGet(r => r.HasFormContentType).Returns(new FormFeature(request.Object).HasFormContentType);
-
- var context = new Mock();
- context.SetupGet(c => c.Request).Returns(request.Object);
+ var context = new DefaultHttpContext();
+ context.Request.ContentType = contentType;
return new ValueProviderFactoryContext(
- context.Object,
+ context,
new Dictionary(StringComparer.OrdinalIgnoreCase));
}
}
}
-#endif
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs
new file mode 100644
index 0000000000..c95b2691d5
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs
@@ -0,0 +1,135 @@
+// 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 System.Threading.Tasks;
+using Microsoft.AspNet.Http.Internal;
+using Xunit;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding.Test
+{
+ public class JQueryFormValueProviderFactoryTest
+ {
+ private static readonly IDictionary _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" } },
+ };
+
+ [Fact]
+ public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
+ {
+ // Arrange
+ var context = CreateContext("some-content-type", formValues: null);
+ var factory = new JQueryFormValueProviderFactory();
+
+ // Act
+ var result = factory.GetValueProvider(context);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Theory]
+ [InlineData("application/x-www-form-urlencoded")]
+ [InlineData("application/x-www-form-urlencoded;charset=utf-8")]
+ [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
+ [InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
+ public void GetValueProvider_ReturnsExpectedValueProviderInstanceWithCurrentCulture(string contentType)
+ {
+ // Arrange
+ var context = CreateContext(contentType, formValues: null);
+ var factory = new JQueryFormValueProviderFactory();
+
+ // Act
+ var result = factory.GetValueProvider(context);
+
+ // Assert
+ var valueProvider = Assert.IsType(result);
+ Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture);
+ }
+
+ 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("application/x-www-form-urlencoded", formValues: _backingStore);
+ var factory = new JQueryFormValueProviderFactory();
+
+ // Act
+ var valueProvider = factory.GetValueProvider(context);
+ var result = await valueProvider.GetValueAsync(key);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.NotNull(result.RawValue);
+ var value = Assert.IsType(result.RawValue);
+ Assert.Equal("found", value);
+ }
+
+ private static ValueProviderFactoryContext CreateContext(
+ string contentType,
+ IDictionary formValues)
+ {
+ var context = new DefaultHttpContext();
+ context.Request.ContentType = contentType;
+ if (formValues != null)
+ {
+ context.Request.Form = new FormCollection(formValues);
+ }
+
+ return new ValueProviderFactoryContext(
+ context,
+ new Dictionary(StringComparer.OrdinalIgnoreCase));
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs
new file mode 100644
index 0000000000..4fa1b91d66
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs
@@ -0,0 +1,19 @@
+// 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;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding
+{
+ public class JQueryFormValueProviderTest : EnumerableValueProviderTest
+ {
+ protected override IEnumerableValueProvider GetEnumerableValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
+ {
+ return new JQueryFormValueProvider(bindingSource, values, culture);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs
index a8d51dc2ad..9a3cd94490 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs
@@ -1,283 +1,21 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
-using Xunit;
-namespace Microsoft.AspNet.Mvc.ModelBinding.Test
+namespace Microsoft.AspNet.Mvc.ModelBinding
{
- public class ReadableStringCollectionValueProviderTest
+ public class ReadableStringCollectionValueProviderTest : EnumerableValueProviderTest
{
- private static readonly IReadableStringCollection _backingStore = new ReadableStringCollection(
- new Dictionary
- {
- { "some", new[] { "someValue1", "someValue2" } },
- { "null_value", null },
- { "prefix.name", new[] { "someOtherValue" } },
- { "prefix.null_value", null },
- { "prefix.property1.property", null },
- { "prefix.property2[index]", null },
- { "prefix[index1]", null },
- { "prefix[index1].property1", null },
- { "prefix[index1].property2", null },
- { "prefix[index2].property", null },
- { "[index]", null },
- { "[index].property", null },
- { "[index][anotherIndex]", null },
- });
-
- [Fact]
- public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
+ protected override IEnumerableValueProvider GetEnumerableValueProvider(
+ BindingSource bindingSource,
+ IDictionary values,
+ CultureInfo culture)
{
- // Arrange
- var backingStore = new ReadableStringCollection(new Dictionary());
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, null);
-
- // Act
- var result = await valueProvider.ContainsPrefixAsync(string.Empty);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
- {
- // Arrange
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.ContainsPrefixAsync(string.Empty);
-
- // Assert
- Assert.True(result);
- }
-
- [Fact]
- public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
- {
- // Arrange
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act & Assert
- Assert.True(await valueProvider.ContainsPrefixAsync("some"));
- Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
- Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
- Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
- Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
- }
-
- [Fact]
- public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
- {
- // Arrange
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.ContainsPrefixAsync("biff");
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
- {
- // Arrange
- var expected = new Dictionary
- {
- { "index", "[index]" },
- { "null_value", "null_value" },
- { "prefix", "prefix" },
- { "some", "some" },
- };
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture: null);
-
- // Act
- var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty);
-
- // Assert
- Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
- }
-
- [Fact]
- public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
- {
- // Arrange
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.GetKeysFromPrefixAsync("abc");
-
- // Assert
- Assert.Empty(result);
- }
-
- [Fact]
- public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
- {
- // Arrange
- var expected = new Dictionary
- {
- { "name", "prefix.name" },
- { "null_value", "prefix.null_value" },
- { "property1", "prefix.property1" },
- { "property2", "prefix.property2" },
- { "index1", "prefix[index1]" },
- { "index2", "prefix[index2]" },
- };
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.GetKeysFromPrefixAsync("prefix");
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Fact]
- public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems()
- {
- // Arrange
- var expected = new Dictionary
- {
- { "property", "[index].property" },
- { "anotherIndex", "[index][anotherIndex]" }
- };
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.GetKeysFromPrefixAsync("[index]");
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Fact]
- public async Task GetValueAsync_SingleValue()
- {
- // Arrange
- var culture = new CultureInfo("fr-FR");
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
-
- // Act
- var result = await valueProvider.GetValueAsync("prefix.name");
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal("someOtherValue", result.RawValue);
- Assert.Equal("someOtherValue", result.AttemptedValue);
- Assert.Equal(culture, result.Culture);
- }
-
- [Fact]
- public async Task GetValueAsync_MultiValue()
- {
- // Arrange
- var culture = new CultureInfo("fr-FR");
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
-
- // Act
- var result = await valueProvider.GetValueAsync("some");
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(new[] { "someValue1", "someValue2" }, (IList)result.RawValue);
- Assert.Equal("someValue1,someValue2", result.AttemptedValue);
- Assert.Equal(culture, result.Culture);
- }
-
- [Theory]
- [InlineData("null_value")]
- [InlineData("prefix.null_value")]
- public async Task GetValue_NullValue(string key)
- {
- // Arrange
- var culture = new CultureInfo("fr-FR");
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
-
- // Act
- var result = await valueProvider.GetValueAsync(key);
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public async Task GetValueAsync_NullMultipleValue()
- {
- // Arrange
- var backingStore = new ReadableStringCollection(
- new Dictionary
- {
- { "key", new string[] { null, null, "value" } }
- });
- var culture = new CultureInfo("fr-FR");
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, culture);
-
- // Act
- var result = await valueProvider.GetValueAsync("key");
-
- // Assert
- Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable);
- Assert.Equal(",,value", result.AttemptedValue);
- }
-
- [Fact]
- public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
- {
- // Arrange
- var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- // Act
- var result = await valueProvider.GetValueAsync("prefix");
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void FilterInclude()
- {
- // Arrange
- var provider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- var bindingSource = new BindingSource(
- BindingSource.Query.Id,
- displayName: null,
- isGreedy: true,
- isFromRequest: true);
-
- // Act
- var result = provider.Filter(bindingSource);
-
- // Assert
- Assert.NotNull(result);
- Assert.Same(result, provider);
- }
-
- [Fact]
- public void FilterExclude()
- {
- // Arrange
- var provider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
-
- var bindingSource = new BindingSource(
- "Test",
- displayName: null,
- isGreedy: true,
- isFromRequest: true);
-
- // Act
- var result = provider.Filter(bindingSource);
-
- // Assert
- Assert.Null(result);
+ var backingStore = new ReadableStringCollection(values);
+ return new ReadableStringCollectionValueProvider(bindingSource, backingStore, culture);
}
}
}
diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs
index ecca5e01ba..8170475234 100644
--- a/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs
@@ -3,8 +3,11 @@
using System;
using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
using Xunit;
@@ -12,16 +15,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{
public class TypeConverterModelBinderIntegrationTest
{
- private class Person
- {
- public Address Address { get; set; }
- }
-
- private class Address
- {
- public int Zip { get; set; }
- }
-
[Fact]
public async Task BindProperty_WithData_WithPrefix_GetsBound()
{
@@ -266,9 +259,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
};
// No Data.
- var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
- {
- });
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
var modelState = new ModelStateDictionary();
@@ -284,5 +275,90 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelState.IsValid);
Assert.Empty(modelState.Keys);
}
+
+ public static TheoryData> PersonStoreData
+ {
+ get
+ {
+ return new TheoryData>
+ {
+ new Dictionary
+ {
+ { "name", new[] { "Fred" } },
+ { "address.zip", new[] { "98052" } },
+ { "address.lines", new[] { "line 1", "line 2" } },
+ },
+ new Dictionary
+ {
+ { "address.lines[]", new[] { "line 1", "line 2" } },
+ { "address[].zip", new[] { "98052" } },
+ { "name[]", new[] { "Fred" } },
+ }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(PersonStoreData))]
+ public async Task BindParameter_FromFormData_BindsCorrectly(IDictionary personStore)
+ {
+ // Arrange
+ var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
+ var parameter = new ParameterDescriptor()
+ {
+ Name = "Parameter1",
+ BindingInfo = new BindingInfo(),
+ ParameterType = typeof(Person),
+ };
+
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.Form = new FormCollection(personStore);
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ // ModelBindingResult
+ Assert.NotNull(modelBindingResult);
+ Assert.True(modelBindingResult.IsModelSet);
+
+ // Model
+ var boundPerson = Assert.IsType(modelBindingResult.Model);
+ Assert.NotNull(boundPerson);
+ Assert.Equal("Fred", boundPerson.Name);
+ Assert.NotNull(boundPerson.Address);
+ Assert.Equal(new[] { "line 1", "line 2" }, boundPerson.Address.Lines);
+ Assert.Equal(98052, boundPerson.Address.Zip);
+
+ // ModelState
+ Assert.True(modelState.IsValid);
+
+ Assert.Equal(new[] { "Address.Lines", "Address.Zip", "Name" }, modelState.Keys.ToArray());
+ var entry = modelState["Address.Lines"];
+ Assert.NotNull(entry);
+ Assert.Empty(entry.Errors);
+ Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
+ var result = entry.Value;
+ Assert.NotNull(result);
+ Assert.Equal("line 1,line 2", result.AttemptedValue);
+ Assert.Equal(new[] { "line 1", "line 2" }, result.RawValue);
+ }
+
+ private class Person
+ {
+ public Address Address { get; set; }
+
+ public string Name { get; set; }
+ }
+
+ private class Address
+ {
+ public string[] Lines { get; set; }
+
+ public int Zip { get; set; }
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
index 76e27ec778..7f8e2b6aaa 100644
--- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
@@ -62,10 +62,11 @@ namespace Microsoft.AspNet.Mvc
// Assert
var valueProviders = options.ValueProviderFactories;
- Assert.Equal(3, valueProviders.Count);
+ Assert.Equal(4, valueProviders.Count);
Assert.IsType(valueProviders[0]);
Assert.IsType(valueProviders[1]);
Assert.IsType(valueProviders[2]);
+ Assert.IsType(valueProviders[3]);
}
[Fact]
@@ -174,7 +175,7 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
- var xmlNodeFilter =
+ var xmlNodeFilter =
Assert.IsType(options.ValidationExcludeFilters[i++]);
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
}