OrchardCore icon indicating copy to clipboard operation
OrchardCore copied to clipboard

Running `IContentManager.PublishAsync(..)` outside of `Controller` doesn't trigger Workflow Event

Open SzymonSel opened this issue 6 months ago • 9 comments

Describe the bug

Running IContentManager.PublishAsync(..) outside of Controller doesn't trigger Workflow Event, when executed by Hangfire.io runner.

From my controller I enqueue a task:

hangfireClient.Schedule<GeneratedAlbumContainer>(x => x.ProcessJob(jobModel), new TimeSpan(0, 0, 0 * 30));
public async Task ProcessJob(JobModel jobModel) {
  var job = await _contentManager.GetAsync(jobModel.JobId, VersionOptions.DraftRequired);
  job.Content.Job.Status.Text = "running";
  await _contentManager.UpdateAsync(job);
  await _contentManager.PublishAsync(job);
  await _session.SaveChangesAsync();
}

I tried manualy triggering the workflow event, by adding the following code to the end of ProcessJob:

var name = nameof(ContentPublishedEvent);
var contentEvent = new ContentEventContext() {
    Name = name,
    ContentType = job.ContentType,
    ContentItemId = job.ContentItemId,
    ContentItemVersionId = job.ContentItemVersionId,
};

var input = new Dictionary<string, object>{
    { ContentEventConstants.ContentItemInputKey, job },
    { ContentEventConstants.ContentEventInputKey, contentEvent }
};

await _workflowManager.TriggerEventAsync(name, input, job.ContentItemId);

This result in an NRE:

System.NullReferenceException: Object reference not set to an instance of an object.
at OrchardCore.Templates.Services.PreviewTemplatesProvider.<>c__DisplayClass1_0.<.ctor>b__0()
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)

This, I'm guessing, comes from different DI scopes. How can I modify my code to trigger the workflow event?

SzymonSel avatar Jun 25 '25 14:06 SzymonSel

in general from a background job managed by a library like hangfire.io, you must create a shell scope and then execute anything in that context.

MichaelPetrinolis avatar Jun 25 '25 20:06 MichaelPetrinolis

@MichaelPetrinolis can you elaborate or point me to some documentation? I'm here to learn :)

SzymonSel avatar Jun 25 '25 20:06 SzymonSel

We triaged this issue and set the milestone according to the priority we think is appropriate (see the docs on how we triage and prioritize issues).

This indicates when the core team may start working on it. However, if you'd like to contribute, we'd warmly welcome you to do that anytime. See our guide on contributions here.

github-actions[bot] avatar Jun 26 '25 17:06 github-actions[bot]

I helped you with this on Discord. Please elaborate here what you tried from that.

Piedone avatar Jun 27 '25 22:06 Piedone

Yeah, hi there! So I tried accessing ShellScope.Current and injecting IShellScope. The first was NRE and the other failed to resolve.

I also tried to manualy resolve the dependencies, but this didn't trigger the Workflow events in OC since it's a different context.

SzymonSel avatar Jun 30 '25 12:06 SzymonSel

It seems that this issue didn't really move for quite a while despite us asking the author for further feedback. Is this something you'd like to revisit any time soon or should we close? Please reply.

github-actions[bot] avatar Jul 15 '25 12:07 github-actions[bot]

Hi, circling back on this

SzymonSel avatar Jul 15 '25 13:07 SzymonSel

It seems that this issue didn't really move for quite a while despite us asking the author for further feedback. Is this something you'd like to revisit any time soon or should we close? Please reply.

github-actions[bot] avatar Jul 30 '25 16:07 github-actions[bot]

Describe the bug

Running IContentManager.PublishAsync(..) outside of Controller doesn't trigger Workflow Event, when executed by Hangfire.io runner.

From my controller I enqueue a task:

hangfireClient.Schedule<GeneratedAlbumContainer>(x => x.ProcessJob(jobModel), new TimeSpan(0, 0, 0 * 30));
public async Task ProcessJob(JobModel jobModel) {
  var job = await _contentManager.GetAsync(jobModel.JobId, VersionOptions.DraftRequired);
  job.Content.Job.Status.Text = "running";
  await _contentManager.UpdateAsync(job);
  await _contentManager.PublishAsync(job);
  await _session.SaveChangesAsync();
}

I tried manualy triggering the workflow event, by adding the following code to the end of ProcessJob:

var name = nameof(ContentPublishedEvent);
var contentEvent = new ContentEventContext() {
    Name = name,
    ContentType = job.ContentType,
    ContentItemId = job.ContentItemId,
    ContentItemVersionId = job.ContentItemVersionId,
};

var input = new Dictionary<string, object>{
    { ContentEventConstants.ContentItemInputKey, job },
    { ContentEventConstants.ContentEventInputKey, contentEvent }
};

await _workflowManager.TriggerEventAsync(name, input, job.ContentItemId);

This result in an NRE:

System.NullReferenceException: Object reference not set to an instance of an object.
at OrchardCore.Templates.Services.PreviewTemplatesProvider.<>c__DisplayClass1_0.<.ctor>b__0()
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)

This, I'm guessing, comes from different DI scopes. How can I modify my code to trigger the workflow event?

THe object reference error is likely because there's no httpcontext. you will need to create one like they do for background task triggers.

https://github.com/OrchardCMS/OrchardCore/blob/ad59b78c8f03c6a23cc34e830f9705bc7b884d7f/src/OrchardCore/OrchardCore.Abstractions/BackgroundTasks/ShellContextExtensions.cs#L12

Here's how I trigger events from background workers

 using (ShellScope shellScope = await _shellHost.GetScopeAsync("Default"))
                {
                    await shellScope.UsingAsync(async scope =>
                    {
                        if (scope != null)
                        {
                            _httpContextAccessor.HttpContext = scope.ShellContext.CreateHttpContext();
                            IWorkflowManager manager = scope.ServiceProvider.GetRequiredService<IWorkflowManager>();
                            var messageData = new Dictionary<string, object>
                        {
                            { "value", cr.Message.Value is null ? "" : cr.Message.Value.ToString() },
                            { "key", cr.Message.Key is null ? "" : cr.Message.Key.ToString() },
                            { "topic", cr.Topic }
                        };
                            await manager
                                .TriggerEventAsync(KafkaMessageEvent.EventName, messageData);
                        }
                    });
                }

weirdyang avatar Aug 15 '25 13:08 weirdyang