As far as I know, this is not configurable. Instead of subclassing, I would probably intercept the default resolver:
Edit: Updated to override GetVirtualPath method instead of GetUrl.
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class UrlResolverInitializer : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Services.Intercept<UrlResolver>(
(locator, defaultResolover) => new CustomUrlResolver(defaultResolver));
}
public void Initialize(InitializationEngine context) { }
public void Uninitialize(InitializationEngine context) { }
}
public class CustomUrlResolver : UrlResolver
{
public CustomUrlResolver(UrlResolver defaultUrlResolver)
{
_defaultUrlResolver = defaultUrlResolver;
}
public override VirtualPathData GetVirtualPath(ContentReference contentLink, string language, VirtualPathArguments virtualPathArguments)
{
var virtualPathData = MyGetVirtualPathFromCacheMethod(contentLink, language, virtualPathArguments);
if (virtualPathData == null)
{
virtualPathData = _defaultUrlResolver.GetVirtualPath(contentLink, language, virtualPathArguments);
// Add data to cache here
}
return virtualPathData;
}
}
Thanks, Mattias.
Nice and simple solution, and somewhat like I had imagined it, myself. I will try to squeeze it in before the default Commerce and CMS resolvers.
I guess I just hoped for a built-in solution.
Hello again!
I tested this a bit and it seems that the GetVirtualPath method is what should be overridden and cached. The GetUrl method doesn't seem to be called that often internally.
Beware of that VirtualPathArguments parameter. I would probably let the default resolver take care of more advanced arguments with route values, other actions than Index etc. Maybe something like this:
private bool ShouldCacheVirtualPath(VirtualPathArguments virtualPathArguments)
{
return virtualPathArguments == null
|| (virtualPathArguments.Action == null
|| virtualPathArguments.Action.Equals("index", StringComparison.OrdinalIgnoreCase))
&& virtualPathArguments.RouteValues == null
&& (virtualPathArguments.ContextMode == ContextMode.Default || virtualPathArguments.ContextMode == ContextMode.Undefined);
}
I ended up overriding the abstract UrlResolver class, for now only intercepting the main method, GetVirtualPath.
In that method, I:
I also found that calling Url.ContentLink with a LinkItem does not invoke the stack of URL resolvers.
I am profiling the performance of a website with a decent amount of blocks, images and links on the start page. In general it is fast, even under fair load. I use the Bombardier tool (2000 requests with 20 concurrent connections) for a simple load test on my development machine.
However, what sticks out a lot from the recorded profiler data is that URL resolving accounts for almost all of the call time and an excessive memory usage (around 2 GB during the said load test).
I can see that all links are always resolved from cached content on each request. E.g., calling Url.ContentUrl on a ContentReference to an image, seems to load that content item from cache (or DB on first load), as well as all its parent containers, in order to resolve the URL. Apparently, the resulting URL is not cached, because this process gets repeated for all request in my load test.
Is there a simple way of enabling caching of generated URLs acrosss requests? It could be short-lived caching, or it could be long-lived with content dependency keys.
Or would I have to sub-class DefaultUrlResolver and add such logic?