Scheduling with BackgroundTaskRunner

    In Umbraco 8+ it is possible to run recurring code using the BackgroundTaskRunner. Below is a complete example showing how to register a Task Runner with a component that will regularly empty out the recycle bin every five minutes.

    Be aware you may or may not want this background task code to run on all servers, if you are using Load Balancing with multiple servers - https://our.umbraco.com/Documentation/Getting-Started/Setup/Server-Setup/Load-Balancing/

    using Umbraco.Core;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Logging;
    using Umbraco.Core.Services;
    using Umbraco.Web.Scheduling;
    
    namespace Umbraco.Web.UI
    {
        // We start by setting up a composer and component so our task runner gets registered on application startup
        public class CleanUpYourRoomComposer : ComponentComposer<CleanUpYourRoomComponent>
        {
        }
    
        public class CleanUpYourRoomComponent : IComponent
        {
            private IProfilingLogger _logger;
            private IRuntimeState _runtime;
            private IContentService _contentService;
            private BackgroundTaskRunner<IBackgroundTask> _cleanUpYourRoomRunner;
    
            public CleanUpYourRoomComponent(IProfilingLogger logger, IRuntimeState runtime, IContentService contentService)
            {
                _logger = logger;
                _runtime = runtime;
                _contentService = contentService;
                _cleanUpYourRoomRunner = new BackgroundTaskRunner<IBackgroundTask>("CleanYourRoom", _logger);
            }
    
            public void Initialize()
            {
                int delayBeforeWeStart = 60000; // 60000ms = 1min
                int howOftenWeRepeat = 300000; //300000ms = 5mins
    
                var task = new CleanRoom(_cleanUpYourRoomRunner, delayBeforeWeStart, howOftenWeRepeat, _runtime, _logger, _contentService);
    
                //As soon as we add our task to the runner it will start to run (after its delay period)
                _cleanUpYourRoomRunner.TryAdd(task);
            }
    
            public void Terminate()
            {
            }
        }
    
        // Now we get to define the recurring task
        public class CleanRoom : RecurringTaskBase
        {
            private IRuntimeState _runtime;
            private IProfilingLogger _logger;
            private IContentService _contentService;
    
            public CleanRoom(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayBeforeWeStart, int howOftenWeRepeat, IRuntimeState runtime, IProfilingLogger logger, IContentService contentService)
                : base(runner, delayBeforeWeStart, howOftenWeRepeat)
            {
                _runtime = runtime;
                _logger = logger;
                _contentService = contentService;
            }
    
            public override bool PerformRun()
            {
                var numberOfThingsInBin = _contentService.CountChildren(Constants.System.RecycleBinContent);
    
                _logger.Info<CleanRoom>("Go clean your room - {ServerRole}", _runtime.ServerRole);
                _logger.Info<CleanRoom>("You have {NumberOfThingsInTheBin}", numberOfThingsInBin);
    
                if (_contentService.RecycleBinSmells())
                {
                    // Take out the trash
                    using (_logger.TraceDuration<CleanRoom>("Mum, I am emptying out the bin", "Its all clean now!"))
                    {
                        _contentService.EmptyRecycleBin(userId: -1);
                    }
                }
    
                // If we want to keep repeating - we need to return true
                // But if we run into a problem/error & want to stop repeating - return false
                return true;
            }
    
            public override bool IsAsync => false;
        }
    }
    

    Using RuntimeState

    In the example above you could add the following switch case at the beginning to help determine the server role & thus if you want to run code on that type of server and exit out early.

    // Do not run the code on replicas nor unknown role servers
    // ONLY run for Master server or Single
    switch (_runtime.ServerRole)
    {
        case ServerRole.Replica:
            _logger.Debug<CleanRoom>("Does not run on replica servers.");
            return true; // We return true to try again as the server role may change!
        case ServerRole.Unknown:
            _logger.Debug<CleanRoom>("Does not run on servers with unknown role.");
            return true; // We return true to try again as the server role may change!
    }