Skip to main content

Outline

At a glance:
  • Mechanisms: Background processing and content lifecycle hooks
  • Platform: Built on .NET Dependency Injection in CMS 12
  • Architecture: Master ScheduledJobBase and IContentEvents for scalability

Scheduled jobs and content events are the primary mechanisms for executing background tasks and orchestrating custom business logic within the content lifecycle. In Optimizely CMS 12, these components leverage the native .NET Dependency Injection container and modern middleware architecture. For technical architects, mastering these tools is essential for maintaining data integrity and system scalability in high-volume PaaS environments.

1. Architectural Overview of Scheduled Jobs

Scheduled jobs perform recurring maintenance, data synchronization, or long-running batch operations outside the scope of a standard web request. They execute within the same process as the application but run asynchronously in an anonymous context.

The ScheduledJobBase Foundation

To implement a custom job, a class must inherit from EPiServer.Scheduler.ScheduledJobBase and be decorated with the [ScheduledPlugIn] attribute. This registers the job in the CMS database and makes it manageable via the Admin UI.

  • Execute() Method: The entry point for the job logic. It returns a string that appears in the "Status" column of the job history in the Admin UI.
  • Dependency Injection: Unlike legacy versions, CMS 12 supports full constructor injection for scheduled jobs, enabling clean service consumption without static service location.
  • Graceful Termination: For jobs processing large datasets, overriding the Stop() method is mandatory to respect administrator requests to halt execution mid-run.
C#
[ScheduledPlugIn(DisplayName = "Archive Expired Content", GUID = "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d")] public class ContentArchivingJob : ScheduledJobBase { private bool _stopSignaled; private readonly IContentRepository _repository; public ContentArchivingJob(IContentRepository repository) { _repository = repository; } public override string Execute() { OnStatusChanged("Starting content archival..."); // Logical processing loop foreach (var item in GetItemsToArchive()) { if (_stopSignaled) return "Job stopped manually by administrator."; // Archive logic here... } return "Archival completed successfully."; } public override void Stop() { _stopSignaled = true; } }

2. Technical Governance for Jobs in PaaS

Operating scheduled jobs in a cloud environment requires specific considerations for reliability and user privileges.

Executing with Privileges

Jobs run as Anonymous by default. If an operation requires a specific role (for example, "WebAdmins"), developers must use IPrincipalAccessor to programmatically assign a security principal within the Execute() method to ensure appropriate access rights are active during batch processing.

Scalability and Multi-Instance Environments

In balanced PaaS environments, multiple site instances share a single database. To prevent race conditions, the Optimizely scheduler ensures only one instance executes a specific job at any given time. Developers should ensure underlying logic is idempotent to handle rare cases of server restarts or job retries, preventing duplicate data processing.

Pro tip: Idempotency is a hard requirement for jobs on scaled-out PaaS. Design every job so that running it twice on the same dataset produces the same result as running it once - this makes retries and restarts safe by default.

3. Content Event Handling with IContentEvents

Content events allow developers to hook into the fundamental state changes of content items - pages, blocks, and media. This is the primary method for enforcing content governance and triggering external integrations at defined points in the content lifecycle.

Structure of an Initialization Module

Event handlers are registered within an IInitializableModule. This ensures subscriptions are established during application startup and explicitly unsubscribed during shutdown to prevent memory leaks from dangling delegates.

C#
[InitializableModule] [ModuleDependency(typeof(EPiServer.Web.InitializationModule))] public class EventInitialization : IInitializableModule { public void Initialize(InitializationEngine context) { var events = context.Locate.Advanced.GetInstance<IContentEvents>(); events.SavingContent += OnSavingContent; } public void Uninitialize(InitializationEngine context) { var events = context.Locate.Advanced.GetInstance<IContentEvents>(); events.SavingContent -= OnSavingContent; // Prevent memory leaks } private void OnSavingContent(object sender, ContentEventArgs e) { // Enforce business rules if (e.Content is ArticlePage article && string.IsNullOrEmpty(article.MetaDescription)) { e.CancelAction = true; e.CancelReason = "Meta Description is required for SEO compliance."; } } }

Important: Always unsubscribe in Uninitialize. Event handlers that are never removed hold a reference to the subscriber and prevent garbage collection, causing memory growth over time in long-running PaaS instances.

4. Strategic Usage of Specific Events

Event Name Lifecycle Stage Primary Use Case
SavingContent Pre-persistence Data validation and blocking illegal saves via e.CancelAction
SavedContent Post-persistence Triggering local cache invalidations after a draft is saved
PublishingContent Pre-approval Final business logic checks before content goes live
PublishedContent Post-live External API triggers such as Optimizely Graph sync or CDN purge

5. Performance and Caching Best Practices

  • Asynchronous Execution: Avoid blocking the main PublishedContent thread. Enqueue high-latency calls (external APIs, search indexing) to maintain editorial responsiveness and prevent timeouts.
  • Batching in Jobs: Use batching patterns to minimize database and network IO and reduce memory footprint. Process items in pages rather than loading the full dataset at once.
  • Avoid Recursive Events: Use SaveAction.SkipValidation to prevent infinite event loops when modifying content from within its own event handler.

Conclusion

The integration of scheduled jobs and content events provides the necessary infrastructure for automating complex workflows and maintaining data standards in Optimizely CMS 12. By using constructor injection in jobs and implementing IInitializableModule for event governance, technical teams can ensure a performant, predictable, and secured CMS architecture.