Skip to main content

DNN · IScheduledTask

DNN runs scheduled tasks with its native Scheduler. A class inheriting from SchedulerClient (namespace DotNetNuke.Services.Scheduling) automatically appears in the list once the assembly is registered, and runs at the frequency you define in Host → Schedule.

Schedulers/NeuroonSyncScheduler.cs

using System;
using System.Collections.Generic;
using DotNetNuke.Instrumentation;
using DotNetNuke.Services.Scheduling;

public class NeuroonSyncScheduler : SchedulerClient
{
private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(NeuroonSyncScheduler));

public NeuroonSyncScheduler(ScheduleHistoryItem oItem) : base()
{
ScheduleHistoryItem = oItem;
}

public override void DoWork()
{
try
{
Progressing();

var client = new NeuroonClient();
var products = LoadProductsFromCommerceModule();

var mode = ScheduleHistoryItem.ScheduleSource == ScheduleSource.STARTED_FROM_BEGIN_REQUEST
? NeuroonClient.SyncMode.FULL
: NeuroonClient.SyncMode.INCREMENTAL;

var result = client.SyncProductsAsync(mode, products).GetAwaiter().GetResult();

ScheduleHistoryItem.AddLogNote(
$"Mode: {mode}. " +
$"Synced {result.NewProducts} new, {result.UpdatedProducts} updated, " +
$"{result.Failed} failed. Quota {result.ProductsCount}/{result.ProductsCount + result.RemainingProducts}.");

if (result.Failed > 0)
{
foreach (var err in result.Errors)
{
ScheduleHistoryItem.AddLogNote($"FAIL externalId={err.ExternalId}: {err.Error}");
}
}

ScheduleHistoryItem.Succeeded = true;
Logger.Info($"Neuroon sync OK ({result.NewProducts}+{result.UpdatedProducts})");
}
catch (Exception ex)
{
ScheduleHistoryItem.Succeeded = false;
ScheduleHistoryItem.AddLogNote("Neuroon sync failed: " + ex.Message);
Logger.Error("Neuroon sync failed", ex);
Errored(ref ex);
}
}

private static IEnumerable<NeuroonProduct> LoadProductsFromCommerceModule()
{
// Adapt to your e-commerce module.
// NB_Store example:
// foreach (var p in NBrightBuy.GetActiveProducts(portalId))
// yield return ProductMapper.Map(p);
throw new NotImplementedException("Implement the mapping from your e-commerce module.");
}
}

Register the scheduler

  1. Compile the assembly that contains NeuroonSyncScheduler and deploy it to bin/.

  2. Go to Host → Schedule → Add Item:

    FieldRecommended value
    Friendly nameNeuroon Product Sync
    TypeFull type name → NeuroonSyncScheduler, MyAssembly
    Schedule1 day
    Catch-up EnabledYes
    Retain history30 entries
    Run on eventNone
    Servers(empty = all)
  3. Click UpdateRun Now to validate the first execution.

Citation: scheduler configuration in the DNN end-to-end recipe (Step 4).

Hourly delta sync (optional)

To reduce the lag between product edit and appearance in Neuroon, register a second scheduler with frequency 1 hour that filters by LastUpdatedDate > UtcNow.AddHours(-1):

private static IEnumerable<NeuroonProduct> LoadDeltaProducts()
{
var since = DateTime.UtcNow.AddHours(-1);
return CommerceRepository
.GetProducts()
.Where(p => p.LastUpdatedDate > since)
.Select(ProductMapper.Map);
}

The sync stays INCREMENTAL. Products without changes in the last hour are not sent.

Log notes in ScheduleHistoryItem

AddLogNote writes entries that appear in Host → Schedule → History. Best practices:

  • One line per high-level event (mode, aggregated counters, quota).
  • One line per individual error (top 20, no more, to keep the table from inflating).
  • Never log the API Key.
  • For long-tail incidents, complement with LoggerSource.Instance.GetLogger (DNN Log4Net).

Force FULL from the UI

ScheduleHistoryItem.ScheduleSource indicates how the task was triggered. You can do:

  • STARTED_FROM_BEGIN_REQUEST (manual, Run Now) → FULL.
  • STARTED_FROM_SCHEDULER (cron) → INCREMENTAL.

This avoids the nightly job marking products as inactive by accident, and lets you force a full re-bootstrap from the UI.

Diagnosing stuck tasks

If the task stays Stuck in Running:

  1. Check that Errored(ref ex) is invoked in every catch.
  2. Look at wp-content/Logs/SchedulingErrorXX.log (in DNN, Portals/_default/Logs/).
  3. Reduce the batch size or increase the HttpClient timeout.

Next steps