IUrlResolver returning null in Initialization Module but not in Scheduled Job

 

I've a scheduled job that was (and still is) working fine, but I've also refactored the code so that it can be called from a Initialization module, so job can be run from a scheduled task, but should also be run when EpiServer 'boots up'.

The Problem: 

// where urlResolver == IUrlResolver 
// where homePage.Error404Page == ContentReference
string 404Url = _urlResolver.GetUrl(homePage.Error404Page)  // returns null

Why is this only returning null in the initialization module? I thought maybe it has something to do with Initialization Module dependencies, so according to https://gregwiechec.com/2015/11/sort-order-of-module-dependencies/ I also added 'EPiServerUIInitialization' (which is near the bottom of the list) as an extra dependency, but it didn't help

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
[ModuleDependency(typeof(EPiServer.UI.EPiServerUIInitialization))]
public class ErrorPagesInitializationModule : IInitializableModule

Any suggestions to get this working??

#199468 Nov 28, 2018 11:07
  • David Knipe
    Member since: 2008
     

    Hey Noel

    It could be that IUrlResolver may well be initialised but something it depends on hasn't been so returns null. If you wanted to try initialising very late then you can try something like this just to prove if its init depedencies or not:

    [InitializableModule]
        [ModuleDependency(typeof(FrameworkAspNetInitialization))]
        [ModuleDependency(typeof(CmsCoreInitialization))]
        [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
        [ModuleDependency(typeof(EPiServerUIInitialization))]
        [ModuleDependency(typeof(EPiServer.Cms.Shell.InitializableModule))]
        public class ErrorPagesInitializationModule : IInitializableModule
        {

    David

    #199471 Nov 28, 2018 11:37
  •  

    Thanks David, I've tried your suggestion but unfortunately it has not made any difference. The Init Module's UrlResolver still returns null while ScheduleJob's UrlResolver (same code) returns correct URL. :-(

    #199473 Nov 28, 2018 11:52
  • Quan Mai
    Member since: 2011
     

    If you have dependency on a "lower level" initialization module that has dependency on "higher level" initialization modules, you are guaranteed that your initialization module is initialized after all of them are done. So your code is actual correct. But how do you get the instance of IUrlResolver ?

    #199474 Nov 28, 2018 12:01
  • Johan Björnfot
    Member since: 2004
     

    The UrlResolver implementation is using the routes configured in the RouteCollection. That means that until the routes have been registered the urlresolver wont be able to resolve any urls. The Routes are registered after the Initialization has taken place so unfortuntely you wont be able to use IUrlResolver from within an InitializableModule. 

    The earliest point during application startup where routes are registered (and hence IUrlResolver functional) is when event EPiServer.Global.RoutesRegistered is rasied (which will be immeditely after the InitializationModules has executed).

    #199485 Nov 28, 2018 14:15
  • Scott Reed
    Member since: 2010
     

    If i get this right the initialization module is calling the code, so one other option is you could always leave the code just to run through the scheduedlejob and use the IScheduledJobExecutor to excute the job. Hopefully that will set the job to start straight away but by the time it excutes the routes should be mapped. If this doesn't work you could dynamically set a scheudle on the job via the IScheduledJobRepository to say run in a minute or so forcing the job to esentially run when the application is ready.

    #199487 Nov 28, 2018 14:37
  •  

    Hi all, thank you for the responses.

    So as per Johan's advice, I'm not trying to run the code in an initialization module. instead, I'm trying to use the initialization module to kick off the scheduleJob, which is going well on my local machine but is failing in Azure (DXC).

    [InitializableModule]
        [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
        public class ScheduleErrorPageJobModule : IInitializableModule
        {
            #region IInitializableModule members
    
            public void Initialize(InitializationEngine context)
            {
                if (context.HostType != HostType.WebApplication)
                    return;
                var scheduledJobRepo = ServiceLocator.Current.GetInstance<IScheduledJobRepository>();
    
                if (scheduledJobRepo == null)
                {
                    LogManager.GetLogger().Error("ScheduleErrorPageJobModule.Initialize(): IScheduledJobRepository not found");
                    return;
                }
    
                var errorPageJobGuid = new Guid("--guid-in-here--");
                var errorPageJob = scheduledJobRepo.Get(errorPageJobGuid);
    
                if (errorPageJob == null)
                {
                    LogManager.GetLogger().Error("ScheduleErrorPageJobModule.Initialize(): ErrorPageGeneratorJob not found");
                    return;
                }
    
                errorPageJob.IsEnabled = true;
                errorPageJob.NextExecution = DateTime.Now.AddMinutes(Settings.Default.ErrorPageCreationDelayInMinutes);
                scheduledJobRepo.Save(errorPageJob);
            }
    
            public void Uninitialize(InitializationEngine context)
            {
            }
    
            #endregion
    
        }

    So it's working locally but not when it's deployed. any thoughts?

    The excluded guid is defined in the 'ScheduledPlugIn' attribute of the job i'm trying to fetch

    [ScheduledPlugIn(DisplayName = "Generate Error Pages Job", GUID = "---guid-in-here---")]
    #199539 Edited, Nov 29, 2018 18:31
  • Aniket
    Member since: 2015
     

    Maybe you can write a test page that gets you a list of all scheduled jobs using scheduledJobRepo.List() method to see if the GUID matches. 

    #199550 Nov 30, 2018 1:05
  • valdis iljuconoks
    Member since: 2011
     

    one workaround would be to "delay" initialization of particular init module. and do the things "after" 1st request is made to the site. but haven't tried whether at that moment routes will be already registered. will give it a try..

    #199552 Nov 30, 2018 6:05
  •  

    @Aniket I already completly renamed my scheduled job when i realised it didn't have a specific guid assigned to it (I think adding a GUID after initial compile didn't update the database, as the newly assigned GUID to the old class returned nothing), but it fetches the job OK in my local after the rename + new guid.

    @valdis Your suggestion is basically what I'm trying to do. Unless you suggesting I have some "Thread.Sleep()" call inside my init module, which would probably turn my boss several shades of "what on earth is that code doing in there!" :-P Any other ways to delay the init module, my first attempt was to list dependencies that are init'ed last. 

    #199556 Nov 30, 2018 10:24
  •  

    Thanks everyone, this has started working now. Not sure why it was failing, but it's not failing now, so i've just added some extra logging and leaving it at that. Thanks for the help.

    #199560 Nov 30, 2018 12:54
  • Johan Björnfot
    Member since: 2004
     

    If the goal is to execute the job once when the application is started up, then you could do it from an event handler to EPiServer.Global.RoutesRegistered, that will only be raised once and that is directly after the routes have been registered. Or alterntatively you override Global.RegisterRoutes and start your job after calling base.RegisterRoutes.

    The disadvantage is that you do not have access to the IOC container in the event handler or the overriden method so you would need to use ServiceLocator.Current

    #199562 Nov 30, 2018 14:48
  • valdis iljuconoks
    Member since: 2011
     

    @Noel, I was not suggessting `Thread.Sleep()` :) there is a special mechanism to delay init modules. write post a link once blog post is ready.

    #199576 Edited, Dec 01, 2018 5:15
  • GOSSO
    Member since: 2007
     

    I link it for you Valdis! https://blog.tech-fellow.net/2018/12/01/episerver-init-infrastructure-under-the-hood/

    I would go triggering a schedual job runing once after init (doing that to build a sitemap after release) 

    Regards!

    #199587 Dec 03, 2018 7:54
  • valdis iljuconoks
    Member since: 2011
     

    more info here (https://blog.tech-fellow.net/2018/12/01/episerver-init-infrastructure-under-the-hood/) on things what you can do to get url resolver working in init module.

    #199601 Dec 03, 2018 13:02
  • valdis iljuconoks
    Member since: 2011
     

    oh, thanks @Luc!

    #199602 Dec 03, 2018 13:03