IUrlResolver returning null in Initialization Module but not in Scheduled Job

Vote:
 

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
Vote:
 

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
Vote:
 

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
Vote:
 

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
Vote:
 

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
Vote:
 

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
Vote:
 

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:29
Vote:
 

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
Vote:
 

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
Vote:
 

@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
Vote:
 

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
Vote:
 

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
Vote:
 

@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:14
Vote:
 

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
Vote:
 

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
Vote:
 

oh, thanks @Luc!

#199602
Dec 03, 2018 13:03
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.