I’ve noticed that the ContentPublishingNotification in Umbraco is being triggered twice when publishing content. This causes my INotificationHandler<ContentPublishingNotification> to execute twice, which leads to unintended behavior in my logic.
I would like to understand:
Why does ContentPublishingNotification trigger twice during a single publish action?
Is there a way to prevent this behavior so that my NotificationHandler only runs once per publish event?
Here is my current handler implementation:
public class AuditLogCreatedNotificationHandler : INotificationAsyncHandler<ContentPublishingNotification>
{
public AuditLogCreatedNotificationHandler()
{
}
public async Task HandleAsync(ContentPublishingNotification notification, CancellationToken cancellationToken)
{
// business logic
}
}
Has anyone else experienced this issue? Is there a recommended way to ensure the notification is only triggered once?
Did you ever figure this out?
From what I can tell, the entities affected, the target, and every datapoint within the notification are identical in both instances of the notification, so I see no reason why it should happen twice.
i think thisbehavior is expected in Umbraco and is not a bug.
ContentPublishingNotification is raised as part of the publishing pipeline, not strictly once per UI “Publish” click. During a publish operation Umbraco may internally:
Process multiple cultures/variants
Run validation and publishing passes
Publish multiple entities in a single request
Because of this, the same content can pass through the pipeline more than once, causing the notification handler to execute multiple times.
i suggest to de-duplicate inside the handler, usually by tracking the content Id + VersionId, which uniquely identifies a publish operation.
eg: public async Task HandleAsync(
ContentPublishingNotification notification,
CancellationToken cancellationToken)
{
foreach (var content in notification.PublishedEntities)
{
var key = $“{content.Id}:{content.VersionId}”;
// Skip if this content version was already handled
if (AlreadyHandled(key))
continue;
MarkAsHandled(key);
// Business logic
}