[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.
|
||||
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();
|
||||
try
|
||||
{
|
||||
|
|
@ -266,5 +275,35 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
|||
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());
|
||||
}
|
||||
|
||||
[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(
|
||||
IServiceProvider services,
|
||||
string appRoot)
|
||||
|
|
@ -1002,13 +1034,13 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
|||
return new UrlHelper(actionContext);
|
||||
}
|
||||
|
||||
private static UrlHelper CreateUrlHelper(string appBase, IRouter router)
|
||||
private static TestUrlHelper CreateUrlHelper(string appBase, IRouter router)
|
||||
{
|
||||
var services = CreateServices();
|
||||
var context = CreateHttpContext(services, appBase);
|
||||
var actionContext = CreateActionContext(context, router);
|
||||
|
||||
return new UrlHelper(actionContext);
|
||||
return new TestUrlHelper(actionContext);
|
||||
}
|
||||
|
||||
private static UrlHelper CreateUrlHelperWithRouteCollection(
|
||||
|
|
@ -1095,5 +1127,22 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
|||
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