DNN · IScheduledTask
DNN ejecuta tareas planificadas con su Scheduler nativo. Una clase que herede de SchedulerClient (espacio DotNetNuke.Services.Scheduling) entra automáticamente en la lista al registrar el assembly y se ejecuta según la frecuencia que tú definas en 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()
{
// Adapta a tu módulo de e-commerce.
// Ejemplo NB_Store:
// foreach (var p in NBrightBuy.GetActiveProducts(portalId))
// yield return ProductMapper.Map(p);
throw new NotImplementedException("Implementa el mapeo desde tu módulo de e-commerce.");
}
}
Registrar el scheduler
-
Compila el assembly que contiene
NeuroonSyncSchedulery despliégalo enbin/. -
Entra a Host → Schedule → Add Item:
Campo Valor recomendado 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 (vacío = todos) -
Pulsa Update → Run Now para validar la primera ejecución.
Cita: configuración del scheduler en el recipe DNN end-to-end (Paso 4).
Sync delta horaria (opcional)
Para reducir el lag entre edición de producto y aparición en Neuroon, registra un segundo scheduler con frecuencia 1 hour que filtre por 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);
}
La sync sigue siendo INCREMENTAL. Los productos sin cambios en la última hora no se mandan.
Log notes en ScheduleHistoryItem
AddLogNote escribe entradas que se ven en Host → Schedule → History. Buenas prácticas:
- Una línea por evento de alto nivel (modo, contadores agregados, cuota).
- Una línea por error individual (top 20, no más, para no inflar la tabla).
- No loguear nunca la API Key.
- Para incidentes de larga cola, complementa con
LoggerSource.Instance.GetLogger(DNN Log4Net).
Forzar FULL desde la UI
ScheduleHistoryItem.ScheduleSource indica cómo se disparó la tarea. Puedes hacer:
STARTED_FROM_BEGIN_REQUEST(manual, Run Now) →FULL.STARTED_FROM_SCHEDULER(cron) →INCREMENTAL.
Esto evita que el job nocturno marque productos como inactivos por accidente, y permite forzar un re-bootstrap completo desde la UI.
Diagnóstico de tareas paradas
Si la tarea queda Stuck en Running:
- Comprueba que
Errored(ref ex)se invoca en cada catch. - Mira
wp-content/Logs/SchedulingErrorXX.log(en DNN,Portals/_default/Logs/). - Reduce el batch o aumenta el timeout del
HttpClient.
Próximos pasos
- Cliente C# completo —
NeuroonClientcon Polly + chunk 500. - Razor snippets — embebido del widget y cart bridge.
- Recipe · DNN end-to-end.