[Perf] UrlHelper shares a single RouteValueDictionary across several calls to Action and RouteUrl methods

This commit is contained in:
mnltejaswini 2016-05-19 13:44:54 -07:00
parent d7ccea17ce
commit df4eb283b2
1 changed files with 37 additions and 2 deletions

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -18,6 +19,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
// Perf: Share the StringBuilder object across multiple calls of GenerateURL for this UrlHelper // Perf: Share the StringBuilder object across multiple calls of GenerateURL for this UrlHelper
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
// Perf: Reuse the RouteValueDictionary across multiple calls of Action for this UrlHelper
private readonly RouteValueDictionary _routeValueDictionary;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and /// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and
@ -32,6 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
} }
ActionContext = actionContext; ActionContext = actionContext;
_routeValueDictionary = new RouteValueDictionary();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -51,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(actionContext)); throw new ArgumentNullException(nameof(actionContext));
} }
var valuesDictionary = new RouteValueDictionary(actionContext.Values); var valuesDictionary = GetValuesDictionary(actionContext.Values);
if (actionContext.Action == null) if (actionContext.Action == null)
{ {
@ -106,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(routeContext)); throw new ArgumentNullException(nameof(routeContext));
} }
var valuesDictionary = new RouteValueDictionary(routeContext.Values); var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
var virtualPathData = GetVirtualPathData(routeContext.RouteName, valuesDictionary); var virtualPathData = GetVirtualPathData(routeContext.RouteName, valuesDictionary);
return GenerateUrl(routeContext.Protocol, routeContext.Host, virtualPathData, routeContext.Fragment); return GenerateUrl(routeContext.Protocol, routeContext.Host, virtualPathData, routeContext.Fragment);
} }
@ -205,6 +209,37 @@ namespace Microsoft.AspNetCore.Mvc.Routing
}); });
} }
private RouteValueDictionary GetValuesDictionary(object values)
{
// Perf: RouteValueDictionary can be cast to IDictionary<string, object>, but it is
// special cased to avoid allocating boxed Enumerator.
var routeValuesDictionary = values as RouteValueDictionary;
if (routeValuesDictionary != null)
{
_routeValueDictionary.Clear();
foreach (var kvp in routeValuesDictionary)
{
_routeValueDictionary.Add(kvp.Key, kvp.Value);
}
return _routeValueDictionary;
}
var dictionaryValues = values as IDictionary<string, object>;
if (dictionaryValues != null)
{
_routeValueDictionary.Clear();
foreach (var kvp in dictionaryValues)
{
_routeValueDictionary.Add(kvp.Key, kvp.Value);
}
return _routeValueDictionary;
}
return new RouteValueDictionary(values);
}
private StringBuilder GetStringBuilder() private StringBuilder GetStringBuilder()
{ {
if(_stringBuilder == null) if(_stringBuilder == null)