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
-
Compile the assembly that contains
NeuroonSyncSchedulerand deploy it tobin/. -
Go to Host → Schedule → Add Item:
Field Recommended value Friendly name Neuroon Product SyncType Full type name → NeuroonSyncScheduler, MyAssemblySchedule 1 day Catch-up Enabled Yes Retain history 30 entries Run on event None Servers (empty = all) -
Click Update → Run 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:
- Check that
Errored(ref ex)is invoked in every catch. - Look at
wp-content/Logs/SchedulingErrorXX.log(in DNN,Portals/_default/Logs/). - Reduce the batch size or increase the
HttpClienttimeout.
Next steps
- Full C# client —
NeuroonClientwith Polly + chunk 500. - Razor snippets — widget embed and cart bridge.
- Recipe · DNN end-to-end.