diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/RouteValueDictionary.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/RouteValueDictionary.cs index ab1f798e95..378e2d3a12 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/RouteValueDictionary.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/RouteValueDictionary.cs @@ -83,6 +83,25 @@ namespace Microsoft.AspNetCore.Routing return; } + var stringValueEnumerable = values as IEnumerable>; + if (stringValueEnumerable != null) + { + var listStorage = new ListStorage(); + _storage = listStorage; + foreach (var kvp in stringValueEnumerable) + { + if (listStorage.ContainsKey(kvp.Key)) + { + var message = Resources.FormatRouteValueDictionary_DuplicateKey(kvp.Key, nameof(RouteValueDictionary)); + throw new ArgumentException(message, nameof(values)); + } + + listStorage._inner.Add(new KeyValuePair(kvp.Key, kvp.Value)); + } + + return; + } + if (values != null) { _storage = new PropertyStorage(values); diff --git a/test/Microsoft.AspNetCore.Mvc.Routing.Abstractions.Tests/RouteValueDictionaryTests.cs b/test/Microsoft.AspNetCore.Mvc.Routing.Abstractions.Tests/RouteValueDictionaryTests.cs index 593fcff191..58ce0b87e8 100644 --- a/test/Microsoft.AspNetCore.Mvc.Routing.Abstractions.Tests/RouteValueDictionaryTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Routing.Abstractions.Tests/RouteValueDictionaryTests.cs @@ -109,6 +109,25 @@ namespace Microsoft.AspNetCore.Routing.Tests } } + public static IEnumerable IEnumerableStringValuePairData + { + get + { + var routeValues = new[] + { + new KeyValuePair("First Name", "James"), + new KeyValuePair("Last Name", "Henrik"), + new KeyValuePair("Middle Name", "Bob") + }; + + yield return new object[] { routeValues.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }; + + yield return new object[] { routeValues.ToList() }; + + yield return new object[] { routeValues }; + } + } + [Theory] [MemberData(nameof(IEnumerableKeyValuePairData))] public void CreateFromIEnumerableKeyValuePair_CopiesValues(object values) @@ -131,6 +150,22 @@ namespace Microsoft.AspNetCore.Routing.Tests kvp => { Assert.Equal("Name", kvp.Key); Assert.Equal("James", kvp.Value); }); } + [Theory] + [MemberData(nameof(IEnumerableStringValuePairData))] + public void CreateFromIEnumerableStringValuePair_CopiesValues(object values) + { + // Arrange & Act + var dict = new RouteValueDictionary(values); + + // Assert + Assert.IsType(dict._storage); + Assert.Collection( + dict.OrderBy(kvp => kvp.Key), + kvp => { Assert.Equal("First Name", kvp.Key); Assert.Equal("James", kvp.Value); }, + kvp => { Assert.Equal("Last Name", kvp.Key); Assert.Equal("Henrik", kvp.Value); }, + kvp => { Assert.Equal("Middle Name", kvp.Key); Assert.Equal("Bob", kvp.Value); }); + } + [Fact] public void CreateFromIEnumerableKeyValuePair_ThrowsExceptionForDuplicateKey() { @@ -148,6 +183,23 @@ namespace Microsoft.AspNetCore.Routing.Tests $"An element with the key 'Name' already exists in the {nameof(RouteValueDictionary)}."); } + [Fact] + public void CreateFromIEnumerableStringValuePair_ThrowsExceptionForDuplicateKey() + { + // Arrange + var values = new List>() + { + new KeyValuePair("name", "Billy"), + new KeyValuePair("Name", "Joey"), + }; + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => new RouteValueDictionary(values), + "values", + $"An element with the key 'Name' already exists in the {nameof(RouteValueDictionary)}."); + } + [Fact] public void CreateFromObject_CopiesPropertiesFromAnonymousType() {