[Perf] Fast Generate Url for specific cases to avoid string allocations
This commit is contained in:
parent
0f8542e4b2
commit
74357b3ed8
|
|
@ -233,6 +233,15 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
// VirtualPathData.VirtualPath returns string.Empty instead of null.
|
// VirtualPathData.VirtualPath returns string.Empty instead of null.
|
||||||
Debug.Assert(pathData.VirtualPath != null);
|
Debug.Assert(pathData.VirtualPath != null);
|
||||||
|
|
||||||
|
// Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
|
||||||
|
// In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
|
||||||
|
// For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
|
||||||
|
string url;
|
||||||
|
if (TryFastGenerateUrl(protocol, host, pathData, fragment, out url))
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
var builder = GetStringBuilder();
|
var builder = GetStringBuilder();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -266,5 +275,35 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
builder.Clear();
|
builder.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryFastGenerateUrl(
|
||||||
|
string protocol,
|
||||||
|
string host,
|
||||||
|
VirtualPathData pathData,
|
||||||
|
string fragment,
|
||||||
|
out string url)
|
||||||
|
{
|
||||||
|
var pathBase = HttpContext.Request.PathBase;
|
||||||
|
url = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(protocol)
|
||||||
|
&& string.IsNullOrEmpty(host)
|
||||||
|
&& string.IsNullOrEmpty(fragment)
|
||||||
|
&& !pathBase.HasValue)
|
||||||
|
{
|
||||||
|
if (pathData.VirtualPath.Length == 0)
|
||||||
|
{
|
||||||
|
url = "/";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (pathData.VirtualPath.StartsWith("/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
url = pathData.VirtualPath;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -939,6 +939,38 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
Assert.Equal(expected, builder.ToString());
|
Assert.Equal(expected, builder.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null, null, null, "/", null, "/")]
|
||||||
|
[InlineData(null, null, null, "/", null, "/")]
|
||||||
|
[InlineData(null, null, null, "/Hello", null, "/Hello" )]
|
||||||
|
[InlineData(null, null, null, "Hello", null, "/Hello")]
|
||||||
|
[InlineData(null, null, null, "/Hello", null, "/Hello")]
|
||||||
|
[InlineData("/", null, null, "", null, "/")]
|
||||||
|
[InlineData("/hello/", null, null, "/world", null, "/hello/world")]
|
||||||
|
[InlineData("/hello/", "https", "myhost", "/world", "fragment-value", "https://myhost/hello/world#fragment-value")]
|
||||||
|
public void GenerateUrl_FastAndSlowPathsReturnsExpected(
|
||||||
|
string appBase,
|
||||||
|
string protocol,
|
||||||
|
string host,
|
||||||
|
string virtualPath,
|
||||||
|
string fragment,
|
||||||
|
string expected)
|
||||||
|
{
|
||||||
|
// Arrage
|
||||||
|
var router = Mock.Of<IRouter>();
|
||||||
|
var pathData = new VirtualPathData(router, virtualPath)
|
||||||
|
{
|
||||||
|
VirtualPath = virtualPath
|
||||||
|
};
|
||||||
|
var urlHelper = CreateUrlHelper(appBase, router);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url = urlHelper.GenerateUrl(protocol, host, pathData, fragment);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, url);
|
||||||
|
}
|
||||||
|
|
||||||
private static HttpContext CreateHttpContext(
|
private static HttpContext CreateHttpContext(
|
||||||
IServiceProvider services,
|
IServiceProvider services,
|
||||||
string appRoot)
|
string appRoot)
|
||||||
|
|
@ -1002,13 +1034,13 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
return new UrlHelper(actionContext);
|
return new UrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UrlHelper CreateUrlHelper(string appBase, IRouter router)
|
private static TestUrlHelper CreateUrlHelper(string appBase, IRouter router)
|
||||||
{
|
{
|
||||||
var services = CreateServices();
|
var services = CreateServices();
|
||||||
var context = CreateHttpContext(services, appBase);
|
var context = CreateHttpContext(services, appBase);
|
||||||
var actionContext = CreateActionContext(context, router);
|
var actionContext = CreateActionContext(context, router);
|
||||||
|
|
||||||
return new UrlHelper(actionContext);
|
return new TestUrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UrlHelper CreateUrlHelperWithRouteCollection(
|
private static UrlHelper CreateUrlHelperWithRouteCollection(
|
||||||
|
|
@ -1095,5 +1127,22 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestUrlHelper : UrlHelper
|
||||||
|
{
|
||||||
|
public TestUrlHelper(ActionContext actionContext) :
|
||||||
|
base(actionContext)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public new string GenerateUrl(string protocol, string host, VirtualPathData pathData, string fragment)
|
||||||
|
{
|
||||||
|
return base.GenerateUrl(
|
||||||
|
protocol,
|
||||||
|
host,
|
||||||
|
pathData,
|
||||||
|
fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue