diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln
index fc578eb8a3..bf9b67107e 100644
--- a/HttpAbstractions.sln
+++ b/HttpAbstractions.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.10
MinimumVisualStudioVersion = 15.0.26730.03
@@ -70,6 +70,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Core.Test", "test\Microsoft.AspNetCore.Authentication.Core.Test\Microsoft.AspNetCore.Authentication.Core.Test.csproj", "{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{5C05BDE0-6339-40BF-8215-97AFA72DCFE1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.Performance", "benchmarks\Microsoft.AspNetCore.Http.Performance\Microsoft.AspNetCore.Http.Performance.csproj", "{4633ADE6-7A61-44EB-B7CE-0141C009DBAA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -272,6 +276,18 @@ Global
{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|x86.ActiveCfg = Release|Any CPU
{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7}.Release|x86.Build.0 = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -296,6 +312,7 @@ Global
{3D8C9A87-5DFB-4EC0-9CB6-174AD3B33852} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
{73CA3145-91BD-4DA5-BC74-40008DE7EA98} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
{A85950C5-2794-47E2-8EAA-05A1DC7C6DA7} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21}
+ {4633ADE6-7A61-44EB-B7CE-0141C009DBAA} = {5C05BDE0-6339-40BF-8215-97AFA72DCFE1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D9A9994D-F09F-4209-861B-4A9036485D1F}
diff --git a/benchmarks/Microsoft.AspNetCore.Http.Performance/Microsoft.AspNetCore.Http.Performance.csproj b/benchmarks/Microsoft.AspNetCore.Http.Performance/Microsoft.AspNetCore.Http.Performance.csproj
new file mode 100644
index 0000000000..2cb0d4f30d
--- /dev/null
+++ b/benchmarks/Microsoft.AspNetCore.Http.Performance/Microsoft.AspNetCore.Http.Performance.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netcoreapp2.2
+ Exe
+ true
+ true
+ false
+ Microsoft.AspNetCore.Http
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/benchmarks/Microsoft.AspNetCore.Http.Performance/Properties/AssemblyInfo.cs b/benchmarks/Microsoft.AspNetCore.Http.Performance/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..2efc4cb5fb
--- /dev/null
+++ b/benchmarks/Microsoft.AspNetCore.Http.Performance/Properties/AssemblyInfo.cs
@@ -0,0 +1 @@
+[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]
\ No newline at end of file
diff --git a/benchmarks/Microsoft.AspNetCore.Http.Performance/RouteValueDictionaryBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Http.Performance/RouteValueDictionaryBenchmark.cs
new file mode 100644
index 0000000000..c256d31a55
--- /dev/null
+++ b/benchmarks/Microsoft.AspNetCore.Http.Performance/RouteValueDictionaryBenchmark.cs
@@ -0,0 +1,182 @@
+// 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
+ {
+ private RouteValueDictionary _arrayValues;
+ private RouteValueDictionary _propertyValues;
+
+ // We modify the route value dictionaries in many of these benchmarks.
+ [IterationSetup]
+ 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
+ {
+ { "action", "Index" }
+ };
+ return dictionary;
+ }
+
+ [Benchmark]
+ public RouteValueDictionary AddThreeItems()
+ {
+ var dictionary = new RouteValueDictionary
+ {
+ { "action", "Index" },
+ { "controller", "Home" },
+ { "id", "15" }
+ };
+ return dictionary;
+ }
+
+ [Benchmark]
+ public RouteValueDictionary ConditionalAdd_ContainsKeyAdd()
+ {
+ var dictionary = _arrayValues;
+
+ if (!dictionary.ContainsKey("action"))
+ {
+ dictionary.Add("action", "Index");
+ }
+
+ if (!dictionary.ContainsKey("controller"))
+ {
+ dictionary.Add("controller", "Home");
+ }
+
+ if (!dictionary.ContainsKey("area"))
+ {
+ dictionary.Add("area", "Admin");
+ }
+
+ return dictionary;
+ }
+
+ [Benchmark]
+ public RouteValueDictionary ConditionalAdd_TryAdd()
+ {
+ var dictionary = _arrayValues;
+
+ dictionary.TryAdd("action", "Index");
+ dictionary.TryAdd("controller", "Home");
+ dictionary.TryAdd("area", "Admin");
+
+ 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 = _propertyValues;
+ 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
+ {
+ ["action"] = "Index"
+ };
+ return dictionary;
+ }
+
+ [Benchmark]
+ public RouteValueDictionary SetExistingItem()
+ {
+ var dictionary = _arrayValues;
+ dictionary["action"] = "About";
+ return dictionary;
+ }
+
+ [Benchmark]
+ public RouteValueDictionary SetThreeItems()
+ {
+ var dictionary = new RouteValueDictionary
+ {
+ ["action"] = "Index",
+ ["controller"] = "Home",
+ ["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;
+ }
+ }
+}
diff --git a/build/dependencies.props b/build/dependencies.props
index a3e49fa1f1..500c0656c9 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -3,7 +3,9 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ 0.10.13
3.0.0-alpha1-20180907.9
+ 3.0.0-alpha1-10419
3.0.0-alpha1-10419
3.0.0-alpha1-10419
3.0.0-alpha1-10419
diff --git a/build/repo.props b/build/repo.props
index 17a98ac7e7..71840e75d1 100644
--- a/build/repo.props
+++ b/build/repo.props
@@ -12,4 +12,8 @@
+
+
+ true
+
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Routing/RouteValueDictionary.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Routing/RouteValueDictionary.cs
index f4153881b3..396f79b507 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/Routing/RouteValueDictionary.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/Routing/RouteValueDictionary.cs
@@ -155,9 +155,9 @@ namespace Microsoft.AspNetCore.Routing
{
get
{
- if (string.IsNullOrEmpty(key))
+ if (key == null)
{
- throw new ArgumentNullException(nameof(key));
+ ThrowArgumentNullExceptionForKey();
}
object value;
@@ -167,9 +167,9 @@ namespace Microsoft.AspNetCore.Routing
set
{
- if (string.IsNullOrEmpty(key))
+ if (key == null)
{
- throw new ArgumentNullException(nameof(key));
+ ThrowArgumentNullExceptionForKey();
}
// We're calling this here for the side-effect of converting from properties
@@ -177,7 +177,7 @@ namespace Microsoft.AspNetCore.Routing
// property storage is immutable.
EnsureCapacity(_count);
- var index = FindInArray(key);
+ var index = FindIndex(key);
if (index < 0)
{
EnsureCapacity(_count + 1);
@@ -255,12 +255,12 @@ namespace Microsoft.AspNetCore.Routing
{
if (key == null)
{
- throw new ArgumentNullException(nameof(key));
+ ThrowArgumentNullExceptionForKey();
}
EnsureCapacity(_count + 1);
- var index = FindInArray(key);
+ var index = FindIndex(key);
if (index >= 0)
{
var message = Resources.FormatRouteValueDictionary_DuplicateKey(key, nameof(RouteValueDictionary));
@@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Routing
{
if (key == null)
{
- throw new ArgumentNullException(nameof(key));
+ ThrowArgumentNullExceptionForKey();
}
return TryGetValue(key, out var _);
@@ -362,7 +362,7 @@ namespace Microsoft.AspNetCore.Routing
EnsureCapacity(Count);
- var index = FindInArray(item.Key);
+ var index = FindIndex(item.Key);
var array = _arrayStorage;
if (index >= 0 && EqualityComparer