Benchmarks and improvements to route value dictionary (#577)

* Add benchmarks for RVD

There are the scenarios that are critical for URL matching performance.

* Reimplement RouteValueDictionary

Improves the scenarios with benchmarks by about 30%

* Fix benchmark

* PR feedback

* More feedback and tests
This commit is contained in:
Ryan Nowak 2018-06-26 13:41:49 -07:00 committed by GitHub
parent 60ad99037e
commit bc5f02444b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 427 additions and 535 deletions

View File

@ -0,0 +1,139 @@
// 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 BenchmarkDotNet.Attributes;
namespace Microsoft.AspNetCore.Routing
{
public class RouteValueDictionaryBenchmark
{
// These dictionaries are used by a few tests over and over, so don't modify them destructively.
private RouteValueDictionary _arrayValues;
private RouteValueDictionary _propertyValues;
[GlobalSetup]
public void Setup()
{
_arrayValues = new RouteValueDictionary()
{
{ "action", "Index" },
{ "controller", "Home" },
{ "id", "17" },
};
_propertyValues = new RouteValueDictionary(new { action = "Index", controller = "Home", id = "17" });
}
[Benchmark]
public RouteValueDictionary AddSingleItem()
{
var dictionary = new RouteValueDictionary();
dictionary.Add("action", "Index");
return dictionary;
}
[Benchmark]
public RouteValueDictionary AddThreeItems()
{
var dictionary = new RouteValueDictionary();
dictionary.Add("action", "Index");
dictionary.Add("controller", "Home");
dictionary.Add("id", "15");
return dictionary;
}
[Benchmark]
public RouteValueDictionary ForEachThreeItems_Array()
{
var dictionary = _arrayValues;
foreach (var kvp in dictionary)
{
GC.KeepAlive(kvp.Value);
}
return dictionary;
}
[Benchmark]
public RouteValueDictionary ForEachThreeItems_Properties()
{
var dictionary = _arrayValues;
foreach (var kvp in dictionary)
{
GC.KeepAlive(kvp.Value);
}
return dictionary;
}
[Benchmark]
public RouteValueDictionary GetThreeItems_Array()
{
var dictionary = _arrayValues;
GC.KeepAlive(dictionary["action"]);
GC.KeepAlive(dictionary["controller"]);
GC.KeepAlive(dictionary["id"]);
return dictionary;
}
[Benchmark]
public RouteValueDictionary GetThreeItems_Properties()
{
var dictionary = _propertyValues;
GC.KeepAlive(dictionary["action"]);
GC.KeepAlive(dictionary["controller"]);
GC.KeepAlive(dictionary["id"]);
return dictionary;
}
[Benchmark]
public RouteValueDictionary SetSingleItem()
{
var dictionary = new RouteValueDictionary();
dictionary["action"] = "Index";
return dictionary;
}
[Benchmark]
public RouteValueDictionary SetExistingItem()
{
var dictionary = _arrayValues;
dictionary["action"] = "About";
return dictionary;
}
[Benchmark]
public RouteValueDictionary SetThreeItems()
{
var dictionary = new RouteValueDictionary();
dictionary["action"] = "Index";
dictionary["controller"] = "Home";
dictionary["id"] = "15";
return dictionary;
}
[Benchmark]
public RouteValueDictionary TryGetValueThreeItems_Array()
{
var dictionary = _arrayValues;
dictionary.TryGetValue("action", out var action);
dictionary.TryGetValue("controller", out var controller);
dictionary.TryGetValue("id", out var id);
GC.KeepAlive(action);
GC.KeepAlive(controller);
GC.KeepAlive(id);
return dictionary;
}
[Benchmark]
public RouteValueDictionary TryGetValueThreeItems_Properties()
{
var dictionary = _propertyValues;
dictionary.TryGetValue("action", out var action);
dictionary.TryGetValue("controller", out var controller);
dictionary.TryGetValue("id", out var id);
GC.KeepAlive(action);
GC.KeepAlive(controller);
GC.KeepAlive(id);
return dictionary;
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Routing.Abstractions;
using Microsoft.Extensions.Internal;
@ -16,14 +17,19 @@ namespace Microsoft.AspNetCore.Routing
/// </summary>
public class RouteValueDictionary : IDictionary<string, object>, IReadOnlyDictionary<string, object>
{
internal Storage _storage;
// 4 is a good default capacity here because that leaves enough space for area/controller/action/id
private const int DefaultCapacity = 4;
internal KeyValuePair<string, object>[] _arrayStorage;
internal PropertyStorage _propertyStorage;
private int _count;
/// <summary>
/// Creates an empty <see cref="RouteValueDictionary"/>.
/// </summary>
public RouteValueDictionary()
{
_storage = EmptyStorage.Instance;
_arrayStorage = Array.Empty<KeyValuePair<string, object>>();
}
/// <summary>
@ -40,63 +46,45 @@ namespace Microsoft.AspNetCore.Routing
/// Only public instance non-index properties are considered.
/// </remarks>
public RouteValueDictionary(object values)
: this()
{
var dictionary = values as RouteValueDictionary;
if (dictionary != null)
if (values is RouteValueDictionary dictionary)
{
var listStorage = dictionary._storage as ListStorage;
if (listStorage != null)
{
_storage = new ListStorage(listStorage);
return;
}
var propertyStorage = dictionary._storage as PropertyStorage;
if (propertyStorage != null)
if (dictionary._propertyStorage != null)
{
// PropertyStorage is immutable so we can just copy it.
_storage = dictionary._storage;
_propertyStorage = dictionary._propertyStorage;
_count = dictionary._count;
return;
}
// If we get here, it's an EmptyStorage.
_storage = EmptyStorage.Instance;
var other = dictionary._arrayStorage;
var storage = new KeyValuePair<string, object>[other.Length];
if (dictionary._count != 0)
{
Array.Copy(other, 0, storage, 0, dictionary._count);
}
_arrayStorage = storage;
_count = dictionary._count;
return;
}
var keyValueEnumerable = values as IEnumerable<KeyValuePair<string, object>>;
if (keyValueEnumerable != null)
if (values is IEnumerable<KeyValuePair<string, object>> keyValueEnumerable)
{
var listStorage = new ListStorage();
_storage = listStorage;
foreach (var kvp in keyValueEnumerable)
{
if (listStorage.ContainsKey(kvp.Key))
{
var message = Resources.FormatRouteValueDictionary_DuplicateKey(kvp.Key, nameof(RouteValueDictionary));
throw new ArgumentException(message, nameof(values));
}
listStorage.Add(kvp);
Add(kvp.Key, kvp.Value);
}
return;
}
var stringValueEnumerable = values as IEnumerable<KeyValuePair<string, string>>;
if (stringValueEnumerable != null)
if (values is IEnumerable<KeyValuePair<string, string>> stringValueEnumerable)
{
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.Add(new KeyValuePair<string, object>(kvp.Key, kvp.Value));
Add(kvp.Key, kvp.Value);
}
return;
@ -104,11 +92,11 @@ namespace Microsoft.AspNetCore.Routing
if (values != null)
{
_storage = new PropertyStorage(values);
var storage = new PropertyStorage(values);
_propertyStorage = storage;
_count = storage.Properties.Length;
return;
}
_storage = EmptyStorage.Instance;
}
/// <inheritdoc />
@ -133,10 +121,21 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(key));
}
if (!_storage.TrySetValue(key, value))
// We're calling this here for the side-effect of converting from properties
// to array. We need to create the array even if we just set an existing value since
// property storage is immutable.
EnsureCapacity(_count);
var index = FindInArray(key);
if (index < 0)
{
Upgrade();
_storage.TrySetValue(key, value);
EnsureCapacity(_count + 1);
_arrayStorage[_count++] = new KeyValuePair<string, object>(key, value);
}
else
{
_arrayStorage[index] = new KeyValuePair<string, object>(key, value);
}
}
}
@ -150,7 +149,7 @@ namespace Microsoft.AspNetCore.Routing
public IEqualityComparer<string> Comparer => StringComparer.OrdinalIgnoreCase;
/// <inheritdoc />
public int Count => _storage.Count;
public int Count => _count;
/// <inheritdoc />
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
@ -160,13 +159,13 @@ namespace Microsoft.AspNetCore.Routing
{
get
{
Upgrade();
EnsureCapacity(_count);
var list = (ListStorage)_storage;
var keys = new string[list.Count];
var array = _arrayStorage;
var keys = new string[_count];
for (var i = 0; i < keys.Length; i++)
{
keys[i] = list[i].Key;
keys[i] = array[i].Key;
}
return keys;
@ -186,13 +185,13 @@ namespace Microsoft.AspNetCore.Routing
{
get
{
Upgrade();
EnsureCapacity(_count);
var list = (ListStorage)_storage;
var values = new object[list.Count];
var array = _arrayStorage;
var values = new object[_count];
for (var i = 0; i < values.Length; i++)
{
values[i] = list[i].Value;
values[i] = array[i].Value;
}
return values;
@ -221,55 +220,43 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(key));
}
Upgrade();
EnsureCapacity(_count + 1);
var list = (ListStorage)_storage;
for (var i = 0; i < list.Count; i++)
var index = FindInArray(key);
if (index >= 0)
{
if (string.Equals(list[i].Key, key, StringComparison.OrdinalIgnoreCase))
{
var message = Resources.FormatRouteValueDictionary_DuplicateKey(key, nameof(RouteValueDictionary));
throw new ArgumentException(message, nameof(key));
}
var message = Resources.FormatRouteValueDictionary_DuplicateKey(key, nameof(RouteValueDictionary));
throw new ArgumentException(message, nameof(key));
}
list.Add(new KeyValuePair<string, object>(key, value));
_arrayStorage[_count] = new KeyValuePair<string, object>(key, value);
_count++;
}
/// <inheritdoc />
public void Clear()
{
if (_storage.Count == 0)
if (_count == 0)
{
return;
}
Upgrade();
if (_propertyStorage != null)
{
_arrayStorage = Array.Empty<KeyValuePair<string, object>>();
_propertyStorage = null;
_count = 0;
return;
}
var list = (ListStorage)_storage;
list.Clear();
Array.Clear(_arrayStorage, 0, _count);
_count = 0;
}
/// <inheritdoc />
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
if (_storage.Count == 0)
{
return false;
}
Upgrade();
var list = (ListStorage)_storage;
for (var i = 0; i < list.Count; i++)
{
if (string.Equals(list[i].Key, item.Key, StringComparison.OrdinalIgnoreCase))
{
return EqualityComparer<object>.Default.Equals(list[i].Value, item.Value);
}
}
return false;
return TryGetValue(item.Key, out var value) && EqualityComparer<object>.Default.Equals(value, item.Value);
}
/// <inheritdoc />
@ -280,7 +267,7 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(key));
}
return _storage.ContainsKey(key);
return TryGetValue(key, out var _);
}
/// <inheritdoc />
@ -298,15 +285,15 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (_storage.Count == 0)
if (Count == 0)
{
return;
}
Upgrade();
EnsureCapacity(Count);
var list = (ListStorage)_storage;
list.CopyTo(array, arrayIndex);
var storage = _arrayStorage;
Array.Copy(storage, 0, array, arrayIndex, _count);
}
/// <inheritdoc />
@ -330,22 +317,21 @@ namespace Microsoft.AspNetCore.Routing
/// <inheritdoc />
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
if (_storage.Count == 0)
if (Count == 0)
{
return false;
}
Upgrade();
EnsureCapacity(Count);
var list = (ListStorage)_storage;
for (var i = 0; i < list.Count; i++)
var index = FindInArray(item.Key);
var array = _arrayStorage;
if (index >= 0 && EqualityComparer<object>.Default.Equals(array[index].Value, item.Value))
{
if (string.Equals(list[i].Key, item.Key, StringComparison.OrdinalIgnoreCase) &&
EqualityComparer<object>.Default.Equals(list[i].Value, item.Value))
{
list.RemoveAt(i);
return true;
}
Array.Copy(array, index + 1, array, index, _count - index);
_count--;
array[_count] = default;
return true;
}
return false;
@ -359,21 +345,22 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(key));
}
if (_storage.Count == 0)
if (Count == 0)
{
return false;
}
Upgrade();
EnsureCapacity(Count);
var list = (ListStorage)_storage;
for (var i = 0; i < list.Count; i++)
var index = FindInArray(key);
if (index >= 0)
{
if (string.Equals(list[i].Key, key, StringComparison.OrdinalIgnoreCase))
{
list.RemoveAt(i);
return true;
}
_count--;
var array = _arrayStorage;
Array.Copy(array, index + 1, array, index, _count - index);
array[_count] = default;
return true;
}
return false;
@ -387,17 +374,95 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(key));
}
return _storage.TryGetValue(key, out value);
if (_propertyStorage != null)
{
var storage = _propertyStorage;
for (var i = 0; i < storage.Properties.Length; i++)
{
if (string.Equals(storage.Properties[i].Name, key, StringComparison.OrdinalIgnoreCase))
{
value = storage.Properties[i].GetValue(storage.Value);
return true;
}
}
value = default;
return false;
}
var array = _arrayStorage;
for (var i = 0; i < _count; i++)
{
if (string.Equals(array[i].Key, key, StringComparison.OrdinalIgnoreCase))
{
value = array[i].Value;
return true;
}
}
value = default;
return false;
}
private void Upgrade()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int capacity)
{
_storage.Upgrade(ref _storage);
if (_propertyStorage != null || _arrayStorage.Length < capacity)
{
EnsureCapacitySlow(capacity);
}
}
private void EnsureCapacitySlow(int capacity)
{
if (_propertyStorage != null)
{
var storage = _propertyStorage;
capacity = Math.Max(storage.Properties.Length, capacity);
var array = new KeyValuePair<string, object>[capacity];
for (var i = 0; i < storage.Properties.Length; i++)
{
var property = storage.Properties[i];
array[i] = new KeyValuePair<string, object>(property.Name, property.GetValue(storage.Value));
}
_arrayStorage = array;
_propertyStorage = null;
return;
}
if (_arrayStorage.Length < capacity)
{
capacity = _arrayStorage.Length == 0 ? DefaultCapacity : _arrayStorage.Length * 2;
var array = new KeyValuePair<string, object>[capacity];
if (_count > 0)
{
Array.Copy(_arrayStorage, 0, array, 0, _count);
}
_arrayStorage = array;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindInArray(string key)
{
var array = _arrayStorage;
for (var i = 0; i < _count; i++)
{
if (string.Equals(array[i].Key, key, StringComparison.OrdinalIgnoreCase))
{
return i;
}
}
return -1;
}
public struct Enumerator : IEnumerator<KeyValuePair<string, object>>
{
private readonly Storage _storage;
private RouteValueDictionary _dictionary;
private int _index;
public Enumerator(RouteValueDictionary dictionary)
@ -407,9 +472,9 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException();
}
_storage = dictionary._storage;
_dictionary = dictionary;
Current = default(KeyValuePair<string, object>);
Current = default;
_index = -1;
}
@ -423,296 +488,50 @@ namespace Microsoft.AspNetCore.Routing
public bool MoveNext()
{
if (++_index < _storage.Count)
if (++_index < _dictionary.Count)
{
Current = _storage[_index];
if (_dictionary._propertyStorage != null)
{
var storage = _dictionary._propertyStorage;
var property = storage.Properties[_index];
Current = new KeyValuePair<string, object>(property.Name, property.GetValue(storage.Value));
return true;
}
Current = _dictionary._arrayStorage[_index];
return true;
}
Current = default(KeyValuePair<string, object>);
Current = default;
return false;
}
public void Reset()
{
Current = default(KeyValuePair<string, object>);
Current = default;
_index = -1;
}
}
// Storage and its subclasses are internal for testing.
internal abstract class Storage
{
public abstract int Count { get; }
public abstract KeyValuePair<string, object> this[int index] { get; set; }
public abstract void Upgrade(ref Storage storage);
public abstract bool TryGetValue(string key, out object value);
public abstract bool ContainsKey(string key);
public abstract bool TrySetValue(string key, object value);
}
internal class ListStorage : Storage
{
private KeyValuePair<string, object>[] _items;
private int _count;
private static readonly KeyValuePair<string, object>[] _emptyArray = new KeyValuePair<string, object>[0];
public ListStorage()
{
_items = _emptyArray;
}
public ListStorage(int capacity)
{
if (capacity == 0)
{
_items = _emptyArray;
}
else
{
_items = new KeyValuePair<string, object>[capacity];
}
}
public ListStorage(ListStorage other)
{
if (other.Count == 0)
{
_items = _emptyArray;
}
else
{
_items = new KeyValuePair<string, object>[other.Count];
for (var i = 0; i < other.Count; i++)
{
this.Add(other[i]);
}
}
}
public int Capacity => _items.Length;
public override int Count => _count;
public override KeyValuePair<string, object> this[int index]
{
get
{
if (index < 0 || index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return _items[index];
}
set
{
if (index < 0 || index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
_items[index] = value;
}
}
public void Add(KeyValuePair<string, object> item)
{
if (_count == _items.Length)
{
EnsureCapacity(_count + 1);
}
_items[_count++] = item;
}
public void RemoveAt(int index)
{
_count--;
for (var i = index; i < _count; i++)
{
_items[i] = _items[i + 1];
}
_items[_count] = default(KeyValuePair<string, object>);
}
public void Clear()
{
for (var i = 0; i < _count; i++)
{
_items[i] = default(KeyValuePair<string, object>);
}
_count = 0;
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
for (var i = 0; i < _count; i++)
{
array[arrayIndex++] = _items[i];
}
}
public override bool ContainsKey(string key)
{
for (var i = 0; i < Count; i++)
{
var kvp = _items[i];
if (string.Equals(key, kvp.Key, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public override bool TrySetValue(string key, object value)
{
for (var i = 0; i < Count; i++)
{
var kvp = _items[i];
if (string.Equals(key, kvp.Key, StringComparison.OrdinalIgnoreCase))
{
_items[i] = new KeyValuePair<string, object>(key, value);
return true;
}
}
Add(new KeyValuePair<string, object>(key, value));
return true;
}
public override bool TryGetValue(string key, out object value)
{
for (var i = 0; i < Count; i++)
{
var kvp = _items[i];
if (string.Equals(key, kvp.Key, StringComparison.OrdinalIgnoreCase))
{
value = kvp.Value;
return true;
}
}
value = null;
return false;
}
public override void Upgrade(ref Storage storage)
{
// Do nothing.
}
private void EnsureCapacity(int min)
{
var newLength = _items.Length == 0 ? 4 : _items.Length * 2;
var newItems = new KeyValuePair<string, object>[newLength];
for (var i = 0; i < _count; i++)
{
newItems[i] = _items[i];
}
_items = newItems;
}
}
internal class PropertyStorage : Storage
internal class PropertyStorage
{
private static readonly PropertyCache _propertyCache = new PropertyCache();
internal readonly object _value;
internal readonly PropertyHelper[] _properties;
public readonly object Value;
public readonly PropertyHelper[] Properties;
public PropertyStorage(object value)
{
Debug.Assert(value != null);
_value = value;
Value = value;
// Cache the properties so we can know if we've already validated them for duplicates.
var type = _value.GetType();
if (!_propertyCache.TryGetValue(type, out _properties))
var type = Value.GetType();
if (!_propertyCache.TryGetValue(type, out Properties))
{
_properties = PropertyHelper.GetVisibleProperties(type);
ValidatePropertyNames(type, _properties);
_propertyCache.TryAdd(type, _properties);
}
}
public PropertyStorage(PropertyStorage propertyStorage)
{
_value = propertyStorage._value;
_properties = propertyStorage._properties;
}
public override int Count => _properties.Length;
public override KeyValuePair<string, object> this[int index]
{
get
{
var property = _properties[index];
return new KeyValuePair<string, object>(property.Name, property.GetValue(_value));
}
set
{
// PropertyStorage never sets a value.
throw new NotImplementedException();
}
}
public override bool TryGetValue(string key, out object value)
{
for (var i = 0; i < _properties.Length; i++)
{
var property = _properties[i];
if (string.Equals(key, property.Name, StringComparison.OrdinalIgnoreCase))
{
value = property.GetValue(_value);
return true;
}
}
value = null;
return false;
}
public override bool ContainsKey(string key)
{
for (var i = 0; i < _properties.Length; i++)
{
var property = _properties[i];
if (string.Equals(key, property.Name, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public override bool TrySetValue(string key, object value)
{
// PropertyStorage never sets a value.
return false;
}
public override void Upgrade(ref Storage storage)
{
storage = new ListStorage(Count);
for (var i = 0; i < _properties.Length; i++)
{
var property = _properties[i];
storage.TrySetValue(property.Name, property.GetValue(_value));
Properties = PropertyHelper.GetVisibleProperties(type);
ValidatePropertyNames(type, Properties);
_propertyCache.TryAdd(type, Properties);
}
}
@ -723,8 +542,7 @@ namespace Microsoft.AspNetCore.Routing
{
var property = properties[i];
PropertyHelper duplicate;
if (names.TryGetValue(property.Name, out duplicate))
if (names.TryGetValue(property.Name, out var duplicate))
{
var message = Resources.FormatRouteValueDictionary_DuplicatePropertyName(
type.FullName,
@ -739,50 +557,6 @@ namespace Microsoft.AspNetCore.Routing
}
}
internal class EmptyStorage : Storage
{
public static readonly EmptyStorage Instance = new EmptyStorage();
private EmptyStorage()
{
}
public override int Count => 0;
public override KeyValuePair<string, object> this[int index]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override bool ContainsKey(string key)
{
return false;
}
public override bool TryGetValue(string key, out object value)
{
value = null;
return false;
}
public override bool TrySetValue(string key, object value)
{
return false;
}
public override void Upgrade(ref Storage storage)
{
storage = new ListStorage();
}
}
private class PropertyCache : ConcurrentDictionary<Type, PropertyHelper[]>
{
}

View File

@ -20,7 +20,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
Assert.Empty(dict._arrayStorage);
Assert.Null(dict._propertyStorage);
}
[Fact]
@ -32,11 +33,12 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
Assert.Empty(dict._arrayStorage);
Assert.Null(dict._propertyStorage);
}
[Fact]
public void CreateFromRouteValueDictionary_WithListStorage_CopiesStorage()
public void CreateFromRouteValueDictionary_WithArrayStorage_CopiesStorage()
{
// Arrange
var other = new RouteValueDictionary()
@ -50,8 +52,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(other, dict);
var storage = Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
var otherStorage = Assert.IsType<RouteValueDictionary.ListStorage>(other._storage);
var storage = Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
var otherStorage = Assert.IsType<KeyValuePair<string, object>[]>(other._arrayStorage);
Assert.NotSame(otherStorage, storage);
}
@ -67,25 +69,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(other, dict);
var storage = Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
var otherStorage = Assert.IsType<RouteValueDictionary.PropertyStorage>(other._storage);
Assert.Same(otherStorage, storage);
}
[Fact]
public void CreateFromRouteValueDictionary_WithEmptyStorage_SharedInstance()
{
// Arrange
var other = new RouteValueDictionary();
// Act
var dict = new RouteValueDictionary(other);
// Assert
Assert.Equal(other, dict);
var storage = Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
var otherStorage = Assert.IsType<RouteValueDictionary.EmptyStorage>(other._storage);
var storage = dict._propertyStorage;
var otherStorage = other._propertyStorage;
Assert.Same(otherStorage, storage);
}
@ -135,7 +120,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(values);
// Assert
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp =>
@ -157,7 +142,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(values);
// Assert
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("First Name", kvp.Key); Assert.Equal("James", kvp.Value); },
@ -178,7 +163,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => new RouteValueDictionary(values),
"values",
"key",
$"An element with the key 'Name' already exists in the {nameof(RouteValueDictionary)}.");
}
@ -195,7 +180,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => new RouteValueDictionary(values),
"values",
"key",
$"An element with the key 'Name' already exists in the {nameof(RouteValueDictionary)}.");
}
@ -209,7 +194,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("awesome", kvp.Key); Assert.Equal(123, kvp.Value); },
@ -226,7 +211,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp =>
@ -252,7 +237,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp =>
@ -273,7 +258,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Empty(dict);
}
@ -287,7 +272,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Empty(dict);
}
@ -301,7 +286,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp =>
@ -328,7 +313,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("DerivedProperty", kvp.Key); Assert.Equal(5, kvp.Value); });
@ -344,7 +329,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
var dict = new RouteValueDictionary(obj);
// Assert
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
Assert.Empty(dict);
}
@ -406,7 +391,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Null(value);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -420,7 +404,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Null(value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -434,7 +418,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -448,11 +432,11 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
public void IndexGet_ListStorage_NoMatch_ReturnsNull()
public void IndexGet_ArrayStorage_NoMatch_ReturnsNull()
{
// Arrange
var dict = new RouteValueDictionary()
@ -465,7 +449,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Null(value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -482,7 +466,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -499,7 +483,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -513,7 +497,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -530,7 +514,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("age", kvp.Key); Assert.Equal(30, kvp.Value); },
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -544,7 +528,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -558,7 +542,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("kEy", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -578,7 +562,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("age", kvp.Key); Assert.Equal(30, kvp.Value); },
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -595,7 +579,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -612,7 +596,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -626,7 +610,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(0, count);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -640,7 +623,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(1, count);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -657,7 +640,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(1, count);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -671,7 +654,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(keys);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -685,7 +668,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(new[] { "key" }, keys);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -702,7 +685,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(new[] { "key" }, keys);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -716,7 +699,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(values);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -730,7 +713,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(new object[] { "value" }, values);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -747,7 +730,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Equal(new object[] { "value" }, values);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -761,7 +744,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -778,7 +761,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("age", kvp.Key); Assert.Equal(30, kvp.Value); },
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -798,7 +781,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("age", kvp.Key); Assert.Equal(30, kvp.Value); },
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -819,7 +802,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -840,7 +823,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
Assert.Collection(
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -860,7 +843,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.OrderBy(kvp => kvp.Key),
kvp => { Assert.Equal("age", kvp.Key); Assert.Equal(30, kvp.Value); },
kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -874,7 +857,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -888,7 +870,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -902,7 +884,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.Null(dict._propertyStorage);
}
[Fact]
@ -919,7 +901,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -938,7 +920,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -957,7 +939,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -976,7 +958,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
// Value comparisons use the default equality comparer.
@ -996,7 +978,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1010,7 +992,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -1024,7 +1005,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1038,7 +1019,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1052,7 +1033,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1069,7 +1050,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1086,7 +1067,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1103,7 +1084,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1128,7 +1109,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
new KeyValuePair<string, object>("key", "value")
},
array);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1148,7 +1129,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1168,7 +1149,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1188,7 +1169,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
// Value comparisons use the default equality comparer.
@ -1209,7 +1190,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1223,7 +1204,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -1238,7 +1218,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1253,7 +1233,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1268,7 +1248,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1283,7 +1263,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1301,7 +1281,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1319,7 +1299,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1337,7 +1317,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Empty(dict);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1353,7 +1333,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Null(value);
Assert.IsType<RouteValueDictionary.EmptyStorage>(dict._storage);
}
[Fact]
@ -1369,7 +1348,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Null(value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1385,7 +1364,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1401,7 +1380,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.PropertyStorage>(dict._storage);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
@ -1420,7 +1399,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.False(result);
Assert.Null(value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1439,7 +1418,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1458,7 +1437,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
// Assert
Assert.True(result);
Assert.Equal("value", value);
Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
@ -1471,8 +1450,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.Add("key", "value");
// Assert 1
var storage = Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.Equal(4, storage.Capacity);
var storage = Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Equal(4, storage.Length);
// Act 2
dict.Add("key2", "value2");
@ -1481,7 +1460,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.Add("key5", "value5");
// Assert 2
Assert.Equal(8, storage.Capacity);
storage = Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Equal(8, storage.Length);
}
[Fact]
@ -1494,20 +1474,19 @@ namespace Microsoft.AspNetCore.Routing.Tests
dict.Add("key3", "value3");
// Assert 1
var storage = Assert.IsType<RouteValueDictionary.ListStorage>(dict._storage);
Assert.Equal(3, storage.Count);
var storage = Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Equal(3, dict.Count);
// Act
dict.Remove("key2");
// Assert 2
Assert.Equal(2, storage.Count);
storage = Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
Assert.Equal(2, dict.Count);
Assert.Equal("key", storage[0].Key);
Assert.Equal("value", storage[0].Value);
Assert.Equal("key3", storage[1].Key);
Assert.Equal("value3", storage[1].Value);
Assert.Throws<ArgumentOutOfRangeException>(() => storage[2]);
}
private class RegularType