Add schedule validation and make dynamic workers replaceable. Introduced DynamicBackgroundWorkerSchedule.Validate (checks Period > 0) and call Validate() in all provider managers (in-memory, Hangfire, Quartz, TickerQ). Switched in-memory dynamic worker storage to ConcurrentDictionary and ensure adding a worker with an existing name stops/removes the previous instance before registering the new one; removals use TryRemove. Updated docs to clarify that adding a worker replaces an existing one and that CronExpression is only supported by scheduler-backed providers. Added tests for replacement behavior and invalid period values.
@ -158,10 +158,10 @@ var updated = await backgroundWorkerManager.UpdateScheduleAsync(
Key points:
Key points:
* `workerName` is the runtime identifier of the dynamic worker.
* `workerName` is the runtime identifier of the dynamic worker. If a worker with the same name already exists, it will be replaced.
* The `handler` is registered at runtime and executed through the provider-specific worker manager.
* The `handler` is registered at runtime and executed through the provider-specific worker manager.
* Provider behavior is preserved. For example, providers with persistent schedulers keep their own scheduling semantics.
* Provider behavior is preserved. For example, providers with persistent schedulers keep their own scheduling semantics.
* The default in-process manager uses in-memory periodic execution.
* The default in-process manager uses in-memory periodic execution based on `Period`. **`CronExpression` is only supported by scheduler-backed providers (Hangfire, Quartz, TickerQ).** The default in-memory provider ignores `CronExpression`.
* `RemoveAsync` stops and removes a dynamic worker. Returns `true` if the worker was found and removed.
* `RemoveAsync` stops and removes a dynamic worker. Returns `true` if the worker was found and removed.
* `UpdateScheduleAsync` changes the schedule of an existing dynamic worker. Returns `true` if the worker was found and updated. The handler itself is not changed.
* `UpdateScheduleAsync` changes the schedule of an existing dynamic worker. Returns `true` if the worker was found and updated. The handler itself is not changed.