Your tutorial makes some jumps that are not clear to me, I get to see a partial Hangfire layout in the settings section, but it looks like css and js are not loading.
For enviroment-specific methods, I would "just" introduce an AppSetting in web.config with the environment name. When you deploy to each environment you can do a web.config transform to set the environment name to the target environment.
Also you could use the runtime state to workout
what type of server you are on (master, slave, standalone) as you probably don't want to run your scheduled task on all the severs
public class MasterServerRegistrar : IServerRegistrar2
{
public IEnumerable<IServerAddress> Registrations
{
get { return Enumerable.Empty<IServerAddress>(); }
}
public ServerRole GetCurrentServerRole()
{
return ServerRole.Master;
}
public string GetCurrentServerUmbracoApplicationUrl()
{
// NOTE: If you want to explicitly define the URL that your application is running on,
// this will be used for the server to communicate with itself, you can return the
// custom path here and it needs to be in this format:
// http://www.mysite.com/umbraco
return null;
}
}
public class FrontEndReadOnlyServerRegistrar : IServerRegistrar2
{
public IEnumerable<IServerAddress> Registrations
{
get { return Enumerable.Empty<IServerAddress>(); }
}
public ServerRole GetCurrentServerRole()
{
return ServerRole.Slave;
}
public string GetCurrentServerUmbracoApplicationUrl()
{
return null;
}
}
and then in a Component, i read something from web.config to work out if it should be a master or slave server. (set when the site is deployed).
var masterOrSlave = ConfigurationManager.AppSettings["MasterSlave"];
if (masterOrSlave.InvariantEquals("Master"))
{
// ServerRegistrarResolver.Current.SetServerRegistrar(new MasterServerRegistrar());
}
else if (masterOrSlave.InvariantEquals("Slave"))
{
ServerRegistrarResolver.Current.SetServerRegistrar(new FrontEndReadOnlyServerRegistrar());
}
I ran into problems trying inject dependencies into the "DoSomething" method. I actually needed the IContentService too, oddly enough. I don't remember the details of whether it just wouldn't compile or if the service wasn't instantiated properly, but I couldn't get it working.
The best (i.e. quickest...this was a project with a tight deadline and budget) solution I came up with was to make a controller action that performed the work I needed done. Inside the "DoSomething" method, I made a webrequest to the url of my controller action. It got the job done and it's been running like a champ in production, every 15 minutes :-)
There are probably better solutions, but I just wanted to share that approach in case someone has a similar issue and this happens to be 'good enough'
In the below example, the values I pull from my ConfigurationHelper class are the url and parameters of the Controller action I created.
public class ScheduleHangfireJobs
{
public void DoSomething()
{
RecurringJob.AddOrUpdate(() => DoIt(null), "*/15 * * * *");
}
public void DoIt(PerformContext context)
{
//Since the import requires umbraco dependencies, it is easier
//to just request it over the web than manage the dependencies in the background
//task.
WebClient client = new WebClient();
var url = ConfigurationHelper.GetConfigSetting<string>("ProductionImportUrl");
var key = ConfigurationHelper.GetConfigSetting<string>("ProductionImportKey");
string downloadString = client.DownloadString($"{url}?key={key}&source=ScheduledJob");
}
}
When i think about it, you would properly have to do it like this.
public class ScheduleHangfireJobs
{
private readonly IContentService _contentService;
public ScheduleHangfireJobs(IContentService contentService)
{
_contentService = contentService;
}
public void DoSomething()
{
RecurringJob.AddOrUpdate(() => DoIt(null), "*/15 * * * *");
}
public void DoIt(PerformContext context)
{
//Since the import requires umbraco dependencies, it is easier
//to just request it over the web than manage the dependencies in the background
//task.
WebClient client = new WebClient();
var url = ConfigurationHelper.GetConfigSetting<string>("ProductionImportUrl");
var key = ConfigurationHelper.GetConfigSetting<string>("ProductionImportKey");
string downloadString = client.DownloadString($"{url}?key={key}&source=ScheduledJob");
}
}
Yep, pretty sure that's what I started with. Again, I don't remember the details of why I bailed, unless it's something like the concrete version of the content service that you'd inject from the startup class not having everything it needs at the point it runs, then errors when you try to use the injected object in your Hangfire class.
I could be totally wrong and maybe I just missed something obvious.
Wow this is cool! A nice alternative to the Hangfire approach. Aside from logs, is there a good way to monitor success/failure when scheduling tasks this way? The Hangfire console is really nice and gives you a history, allows you to run task on demand etc.
Scheduled tasks in Umbraco 8 no longer available?
Hey.
I need to setup a recurring task for our new project. But it looks like scheduled tasks are gone from umbracoSettings.config in version 8?
How do I setup a scheduled task in this new Umbraco version?
Thank you. Kris.
Hi,
I would recommend using https://www.hangfire.io/
Thanks I will check it out
Yep HangFire is recommended at an alternative, the built in facility was removed from v8. :-)
I've blogged about Hangfire as well, Hangfire is fantastic: https://cultiv.nl/blog/using-hangfire-for-scheduled-tasks-in-umbraco/
Your tutorial makes some jumps that are not clear to me, I get to see a partial Hangfire layout in the settings section, but it looks like css and js are not loading.
What's not indicated in your tutorial is that you need to modify the web.config:
After that change it seems to work.
Cheers.
Sebastiaan, Since comments on your blog are closed I just wanted to let you know how much I appreciate your tutorial! Excellent material!
Hi Sebastiaan.
Can you Dependency inject, like below? And how would you differ what methods to run between environments?
I don't know, have you tried it? 🙈
For enviroment-specific methods, I would "just" introduce an
AppSetting
in web.config with the environment name. When you deploy to each environment you can do a web.config transform to set the environment name to the target environment.Hi,
Also you could use the
runtime
state to workout what type of server you are on (master, slave, standalone) as you probably don't want to run your scheduled task on all the severssee https://our.umbraco.com/Documentation/Reference/Scheduling/#using-runtimestate
Hi Kevin.
Is there a way to set the runtime server role?
Hi
Yeah if you mean can i force a server to be master / slave then you can do it via an IServiceRegister class (see https://our.umbraco.com/Documentation/Getting-Started/Setup/Server-Setup/Load-Balancing/flexible-advanced#explicit-master-scheduling-server )
I usally have a bit of code like this :
and then in a Component, i read something from web.config to work out if it should be a master or slave server. (set when the site is deployed).
Kevin
Thanks Kevin, that's very useful and something i will definitely implement.
I ran into problems trying inject dependencies into the "DoSomething" method. I actually needed the IContentService too, oddly enough. I don't remember the details of whether it just wouldn't compile or if the service wasn't instantiated properly, but I couldn't get it working.
The best (i.e. quickest...this was a project with a tight deadline and budget) solution I came up with was to make a controller action that performed the work I needed done. Inside the "DoSomething" method, I made a webrequest to the url of my controller action. It got the job done and it's been running like a champ in production, every 15 minutes :-)
There are probably better solutions, but I just wanted to share that approach in case someone has a similar issue and this happens to be 'good enough'
In the below example, the values I pull from my ConfigurationHelper class are the url and parameters of the Controller action I created.
When i think about it, you would properly have to do it like this.
Yep, pretty sure that's what I started with. Again, I don't remember the details of why I bailed, unless it's something like the concrete version of the content service that you'd inject from the startup class not having everything it needs at the point it runs, then errors when you try to use the injected object in your Hangfire class.
I could be totally wrong and maybe I just missed something obvious.
Scheduled Tasks in Umbraco 8 are handled via classes inherited from RecurringTaskBase. Here is a scheduled task we run on a live U8 project:
Our setup has load balancing so we use the machine name to only set the process to run on a specific machine.
Also we need the settings from "SettingsDataComponent settings", so we ensure that that one is initialised before this one
Wow this is cool! A nice alternative to the Hangfire approach. Aside from logs, is there a good way to monitor success/failure when scheduling tasks this way? The Hangfire console is really nice and gives you a history, allows you to run task on demand etc.
is working on a reply...