In which context does the search provider indexer execute? There is code which should avoid using the RequestCacheUrlResolver if the context is not a web context but apparently the check is buggy.
Until the bug is fixed, you can work around this issue by changing the container configuration using a initializable/configurable module (or append the container config to one you already have):
[ModuleDependency(typeof(EPiServer.Commerce.Initialization.InitializationModule))]
public class FixUrlResolverHttpContext : IConfigurableModule
{
public void Initialize(InitializationEngine context) {}
public void Preload(string[] parameters) {}
public void Uninitialize(InitializationEngine context) {}
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Container.Configure(
c =>
c.For<ServiceAccessor<HttpContextBase>>()
.Use(() => HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null));
}
}
I run it using the Build/Rebuild Index buttons i Commerce Manager. I'll try your fix.
I tried your fix and I no longer get an exception. However, GetUrl always returns null run in commerce manager context.Is this not the correct way to get the url for a product or category page?
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
var url = urlResolver.GetUrl(content.ContentLink, "sv-SE");
Do you get something out of GetUrl if you run it in the front end site context?
Are you really using a language branch for the specific culture sv-SE? Generally the neutral culture sv is used, that is what is set up out of the box. Check the website language settings in CMS admin and the language setup of your catalog.
Also, does the content you want to get the URL for have a matching render template? If it does not, GetUrl returns null.
It works in front end site context but in commerce manager context GetUrl returns null.
We use sv-SE since we have multiple locales for some countries. For example fi-FI and sv-FI
I think I know why now... Your render templates are in your template project, which is not deployed in the manager site. Then no renderer is found for the content and no URL is returned. It could perhaps be solved by deploying the assemblies containing the renderers and content types to the manager site but I don't know if that could cause other side effects. Perhaps deploying an assembly containing a dummy renderer which works for any CatalogContentBase would be an alternative but that is completely untested.
I think we may have a gap in the UrlResolver functionality here. What is it that you need the URL for in the search provider? Would it be sufficient to have permanent links (which you can get from a IPermanentLinkMapper service)?
This is probably a better solution than deploying assemblies:
var requestContext = new RequestContext {RouteData = new RouteData()};
var contentRouteValues = new RouteValueDictionary
{
{RoutingConstants.NodeKey, contentLink},
{RoutingConstants.LanguageKey, language},
};
var virtualPath = RouteTable.Routes.GetVirtualPath(requestContext, contentRouteValues);
var url = virtualPath.GetUrl();
We're indexing the url's since we're using Apptus eSales and don't want to fetch information from several sources.
GetVirtualPath() needs a HttpContext to get the application path so I setup a fake HttpContext. However, for any of our product types the url looks the same except for the id. I tried adding contextmode default to the route data tokens but that didn't change anything.
/ProductInCartOrWishList/Edit?node=90__CatalogContent&language=sv-SE
I think we'll have to get the url front end for now.
Sorry about this, the UrlResolver should be able to work even when there is no template but there's a bug which prevents it. The strange URL you got is because there are more routes registered that apparently can be applied. You should filter by content route, to do something like:
var requestContext = new RequestContext {RouteData = new RouteData()}; //Also set the fake HTTP Context if required
foreach (RouteBase route in RouteTable.Routes.Where(r => r is ContentRoute))
{
// Create new route values for each route in case they are manipulated by the route
var contentRouteValues = new RouteValueDictionary
{
{RoutingConstants.NodeKey, contentLink},
{RoutingConstants.LanguageKey, language},
};
var virtualPath = route.GetVirtualPath(requestContext, contentRouteValues);
if (virtualPath != null)
{
return virtualPath.GetUrl();
}
}
return null;
We never got this working.
We now run the indexer using the scheduled job instead which makes it run in the correct context
However it's giving us another error. Note that we don't use the SeoUri routing but the "Uri name" alternative.
ArgumentNullException: "The provided content link does not have a value.\r\nParameter name: contentLink"
at EPiServer.DataFactory.Get[T](ContentReference contentLink, ILanguageSelector languageSelector)
at EPiServer.DataFactory.Get[T](ContentReference contentLink)
at EPiServer.Web.Routing.Segments.NodeSegment.GetVirtualPathSegment(RequestContext requestContext, RouteValueDictionary values)
at EPiServer.Web.Routing.Segments.NodeSegment.GetVirtualPathSegment(RequestContext requestContext, RouteValueDictionary values, HashSet`1 usedValues)
at EPiServer.Web.Routing.ContentRoute.AddVirtualPathFromSegments(StringBuilder virtualPath, RequestContext requestContext, RouteValueDictionary values, HashSet`1 usedValues, Int32 lastNonDefaultIndex)
at EPiServer.Web.Routing.ContentRoute.GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
at EPiServer.Web.Routing.UrlResolver.GetUrlFromRoute(ContentReference contentRefernce, String language, RouteValueDictionary routeValues, RequestContext requestContext)
at EPiServer.Web.Routing.UrlResolver.GetVirtualPath(ContentReference contentLink, String language, ContextMode contextMode, RouteValueDictionary routeValues, RequestContext requestContext)
at EPiServer.Web.Routing.UrlResolver.GetVirtualPath(ContentReference contentLink, String language, VirtualPathArguments virtualPathArguments)
at EPiServer.Web.Routing.UrlResolver.GetUrl(ContentReference contentLink, String language)
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetVirtualPath(ContentReference contentLink, String language, ContextMode contextMode, Func`1 getVirtualPathAction)
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetUrl(ContentReference contentLink, String language)
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.<>c__DisplayClass1.<GetUrl>b__0()
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetVirtualPath(ContentReference contentLink, String language, ContextMode contextMode, Func`1 getVirtualPathAction)
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetUrl(ContentReference contentLink)
...
private code
...
at Mediachase.Search.SearchManager.BuildIndex(Boolean rebuild)
I suggest you register a support case for this. If possible it would be great if you could include the code for your search provider, or even better some stripped-down repro code which demonstrates the problem on a sample site / catalog.
Thanks in advance!
We saw a similar problem with "contentLink", when trying to use the UrlResolver on catalog content in a scheduled job.
It was solved by adding a wildcard hostname (*) to the site definition.
Mads's solution works like a charm. This issue was raised in 2013, and still exists in the version 9.6
Unfortunatley the "*" doesn't work if you have multiple sites setup, as you can only have one wildcard set for all of your sites. Now what.
Is the issue identical (calling GetUrl from a custom search indexer)? I don't think we ever got the bug report I requested three years ago so there was perhaps never any follow up on this. I'll log the bug myself, just want to make sure I have the correct repro steps.
Not from a search indexer, but from a scheduled job, at least (so no normal HttpContext).
The bug appears to be fixed in our latest version (Commerce 10.1.0). So if possible I suggest you to try and upgrade.
/Q
For future visitors: This might help you http://vimvq1987.com/2017/02/find-indexing-job-hierarchicalcatalogpartialrouter-note/, (There is no guarantee this will fix it for you, as the underlying problem can be different, but you you are registering custom root for hierarchicalcatalogpartialrouter, then it would be worth checking).
I'm trying to use GetUrl() in a commerce search provider indexer and GetUrl throws a null exception.
Doing this:
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
urlResolver.GetUrl(node.ContentLink)
Gives me
Value cannot be null.
Parameter name: httpContext
at System.Web.HttpContextWrapper..ctor(HttpContext httpContext)
at EPiServer.Framework.FrameworkInitialization.<ConfigureContainer>b__1()
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetVirtualPath(ContentReference contentLink, String language, ContextMode contextMode, Func`1 getVirtualPathAction)
at EPiServer.Commerce.Routing.RequestCacheUrlResolver.GetUrl(ContentReference contentLink)
.
(private code)
.
at Mediachase.Search.SearchManager.BuildIndex(Boolean rebuild)