Reduce allocation in URL generation
This change optimizes our a per-operation dictionary that really can just be cached for the whole app's lifetime.
This commit is contained in:
parent
37c167aa74
commit
813171a016
|
|
@ -14,14 +14,15 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
{
|
{
|
||||||
public class TemplateBinder
|
public class TemplateBinder
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyDictionary<string, object> _defaults;
|
private readonly RouteValueDictionary _defaults;
|
||||||
|
private readonly RouteValueDictionary _filters;
|
||||||
private readonly RouteTemplate _template;
|
private readonly RouteTemplate _template;
|
||||||
private readonly UrlEncoder _urlEncoder;
|
private readonly UrlEncoder _urlEncoder;
|
||||||
|
|
||||||
public TemplateBinder(
|
public TemplateBinder(
|
||||||
RouteTemplate template,
|
RouteTemplate template,
|
||||||
UrlEncoder urlEncoder,
|
UrlEncoder urlEncoder,
|
||||||
IReadOnlyDictionary<string, object> defaults)
|
RouteValueDictionary defaults)
|
||||||
{
|
{
|
||||||
if (template == null)
|
if (template == null)
|
||||||
{
|
{
|
||||||
|
|
@ -36,6 +37,14 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
_template = template;
|
_template = template;
|
||||||
_urlEncoder = urlEncoder;
|
_urlEncoder = urlEncoder;
|
||||||
_defaults = defaults;
|
_defaults = defaults;
|
||||||
|
|
||||||
|
// Any default that doesn't have a corresponding parameter is a 'filter' and if a value
|
||||||
|
// is provided for that 'filter' it must match the value in defaults.
|
||||||
|
_filters = new RouteValueDictionary(_defaults);
|
||||||
|
foreach (var parameter in _template.Parameters)
|
||||||
|
{
|
||||||
|
_filters.Remove(parameter.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Get the list of values we're going to try to use to match and generate this URI
|
// Step 1: Get the list of values we're going to try to use to match and generate this URI
|
||||||
|
|
@ -132,25 +141,22 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
|
|
||||||
// Any default values that don't appear as parameters are treated like filters. Any new values
|
// Any default values that don't appear as parameters are treated like filters. Any new values
|
||||||
// provided must match these defaults.
|
// provided must match these defaults.
|
||||||
if (context.Filters != null)
|
foreach (var filter in _filters)
|
||||||
{
|
{
|
||||||
foreach (var filter in context.Filters)
|
var parameter = GetParameter(filter.Key);
|
||||||
|
if (parameter != null)
|
||||||
{
|
{
|
||||||
var parameter = GetParameter(filter.Key);
|
continue;
|
||||||
if (parameter != null)
|
}
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
object value;
|
object value;
|
||||||
if (values.TryGetValue(filter.Key, out value))
|
if (values.TryGetValue(filter.Key, out value))
|
||||||
|
{
|
||||||
|
if (!RoutePartsEqual(value, filter.Value))
|
||||||
{
|
{
|
||||||
if (!RoutePartsEqual(value, filter.Value))
|
// If there is a non-parameterized value in the route and there is a
|
||||||
{
|
// new value for it and it doesn't match, this route won't match.
|
||||||
// If there is a non-parameterized value in the route and there is a
|
return null;
|
||||||
// new value for it and it doesn't match, this route won't match.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,21 +352,14 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
||||||
private class TemplateBindingContext
|
private class TemplateBindingContext
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyDictionary<string, object> _defaults;
|
private readonly RouteValueDictionary _defaults;
|
||||||
|
|
||||||
private readonly RouteValueDictionary _acceptedValues;
|
private readonly RouteValueDictionary _acceptedValues;
|
||||||
private readonly RouteValueDictionary _filters;
|
|
||||||
|
|
||||||
public TemplateBindingContext(IReadOnlyDictionary<string, object> defaults)
|
public TemplateBindingContext(RouteValueDictionary defaults)
|
||||||
{
|
{
|
||||||
_defaults = defaults;
|
_defaults = defaults;
|
||||||
|
|
||||||
_acceptedValues = new RouteValueDictionary();
|
_acceptedValues = new RouteValueDictionary();
|
||||||
|
|
||||||
if (_defaults != null)
|
|
||||||
{
|
|
||||||
_filters = new RouteValueDictionary(_defaults);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RouteValueDictionary AcceptedValues
|
public RouteValueDictionary AcceptedValues
|
||||||
|
|
@ -368,11 +367,6 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
get { return _acceptedValues; }
|
get { return _acceptedValues; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public RouteValueDictionary Filters
|
|
||||||
{
|
|
||||||
get { return _filters; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Accept(string key, object value)
|
public void Accept(string key, object value)
|
||||||
{
|
{
|
||||||
if (!_acceptedValues.ContainsKey(key))
|
if (!_acceptedValues.ContainsKey(key))
|
||||||
|
|
@ -388,7 +382,6 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
object value;
|
object value;
|
||||||
if (_defaults != null && _defaults.TryGetValue(key, out value))
|
if (_defaults != null && _defaults.TryGetValue(key, out value))
|
||||||
{
|
{
|
||||||
_filters.Remove(key);
|
|
||||||
_acceptedValues.Add(key, value);
|
_acceptedValues.Add(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -400,10 +393,7 @@ namespace Microsoft.AspNet.Routing.Template
|
||||||
|
|
||||||
private string DebuggerToString()
|
private string DebuggerToString()
|
||||||
{
|
{
|
||||||
return string.Format(
|
return string.Format("{{Accepted: '{0}'}}", string.Join(", ", _acceptedValues.Keys));
|
||||||
"{{Accepted: '{0}' Filters: '{1}'}}",
|
|
||||||
string.Join(", ", _acceptedValues.Keys),
|
|
||||||
string.Join(", ", _filters?.Keys));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
[MemberData(nameof(EmptyAndNullDefaultValues))]
|
[MemberData(nameof(EmptyAndNullDefaultValues))]
|
||||||
public void Binding_WithEmptyAndNull_DefaultValues(
|
public void Binding_WithEmptyAndNull_DefaultValues(
|
||||||
string template,
|
string template,
|
||||||
IReadOnlyDictionary<string, object> defaults,
|
RouteValueDictionary defaults,
|
||||||
RouteValueDictionary values,
|
RouteValueDictionary values,
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
|
|
@ -255,7 +255,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
[MemberData(nameof(OptionalParamValues))]
|
[MemberData(nameof(OptionalParamValues))]
|
||||||
public void GetVirtualPathWithMultiSegmentWithOptionalParam(
|
public void GetVirtualPathWithMultiSegmentWithOptionalParam(
|
||||||
string template,
|
string template,
|
||||||
IReadOnlyDictionary<string, object> defaults,
|
RouteValueDictionary defaults,
|
||||||
RouteValueDictionary ambientValues,
|
RouteValueDictionary ambientValues,
|
||||||
RouteValueDictionary values,
|
RouteValueDictionary values,
|
||||||
string expected)
|
string expected)
|
||||||
|
|
@ -1083,7 +1083,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
||||||
|
|
||||||
private static void RunTest(
|
private static void RunTest(
|
||||||
string template,
|
string template,
|
||||||
IReadOnlyDictionary<string, object> defaults,
|
RouteValueDictionary defaults,
|
||||||
RouteValueDictionary ambientValues,
|
RouteValueDictionary ambientValues,
|
||||||
RouteValueDictionary values,
|
RouteValueDictionary values,
|
||||||
string expected,
|
string expected,
|
||||||
|
|
|
||||||
|
|
@ -1571,9 +1571,14 @@ namespace Microsoft.AspNet.Routing.Tree
|
||||||
var entry = new TreeRouteLinkGenerationEntry();
|
var entry = new TreeRouteLinkGenerationEntry();
|
||||||
entry.Template = TemplateParser.Parse(template);
|
entry.Template = TemplateParser.Parse(template);
|
||||||
|
|
||||||
var defaults = entry.Template.Parameters
|
var defaults = new RouteValueDictionary();
|
||||||
.Where(p => p.DefaultValue != null)
|
foreach (var parameter in entry.Template.Parameters)
|
||||||
.ToDictionary(p => p.Name, p => p.DefaultValue);
|
{
|
||||||
|
if (parameter.DefaultValue != null)
|
||||||
|
{
|
||||||
|
defaults.Add(parameter.Name, parameter.DefaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var constraintBuilder = new RouteConstraintBuilder(CreateConstraintResolver(), template);
|
var constraintBuilder = new RouteConstraintBuilder(CreateConstraintResolver(), template);
|
||||||
foreach (var parameter in entry.Template.Parameters)
|
foreach (var parameter in entry.Template.Parameters)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue