diff --git a/src/bundles/Elsa.Server.Web/MyEndpoint.cs b/src/bundles/Elsa.Server.Web/MyEndpoint.cs
new file mode 100644
index 000000000..12ff777c2
--- /dev/null
+++ b/src/bundles/Elsa.Server.Web/MyEndpoint.cs
@@ -0,0 +1,27 @@
+using Elsa.Abstractions;
+using Elsa.Workflows.Runtime.Contracts;
+
+namespace Elsa.Server.Web;
+
+public class MyEndpoint : ElsaEndpointWithoutRequest
+{
+ private readonly IEventPublisher _eventPublisher;
+
+ public MyEndpoint(IEventPublisher eventPublisher)
+ {
+ _eventPublisher = eventPublisher;
+ }
+
+ public override void Configure()
+ {
+ Get("/my-event-workflow");
+ AllowAnonymous();
+ }
+
+ public override async Task HandleAsync(CancellationToken ct)
+ {
+ Console.WriteLine("Publishing MyEvent");
+ var results = await _eventPublisher.PublishAsync("MyEvent", cancellationToken: ct);
+ Console.WriteLine($"Affected workflows: {results.Count}");
+ }
+}
\ No newline at end of file
diff --git a/src/bundles/Elsa.Server.Web/MyEventWorkflow.cs b/src/bundles/Elsa.Server.Web/MyEventWorkflow.cs
new file mode 100644
index 000000000..2439e537c
--- /dev/null
+++ b/src/bundles/Elsa.Server.Web/MyEventWorkflow.cs
@@ -0,0 +1,39 @@
+using Elsa.Workflows;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Contracts;
+using Elsa.Workflows.Runtime.Activities;
+
+namespace Elsa.Server.Web;
+
+public class OnMyEventWorkflow : WorkflowBase
+{
+ protected override void Build(IWorkflowBuilder builder)
+ {
+ builder.Version = 1;
+ builder.Id = "OnMyEventWorkflow";
+ builder.Root = new Sequence
+ {
+ Activities =
+ {
+ new Event("MyEvent")
+ {
+ CanStartWorkflow = true
+ },
+ new Inline(async () =>
+ {
+ // IEventPublisher.PublishAsync returns before this executes
+ await SomeCallAsync();
+ }),
+ new WriteLine("End of workflow"),
+ new Finish()
+ }
+ };
+ }
+
+ private async Task SomeCallAsync()
+ {
+ Console.WriteLine("Hello from OnMyEventWorkflow");
+ await Task.Delay(1000);
+ Console.WriteLine("Goodbye from OnMyEventWorkflow");
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInbox.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInbox.cs
index 949c7cf62..52245a1fe 100644
--- a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInbox.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInbox.cs
@@ -32,13 +32,14 @@ public interface IWorkflowInbox
/// The message to deliver.
/// An optional cancellation token.
ValueTask DeliverAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default);
-
+
///
/// Broadcasts the specified message, which may trigger new workflows and resume existing ones.
///
/// The message to broadcast.
+ /// An optional set of delivery options.
/// An optional cancellation token.
- ValueTask BroadcastAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default);
+ ValueTask BroadcastAsync(WorkflowInboxMessage message, BroadcastWorkflowInboxMessageOptions? options, CancellationToken cancellationToken = default);
///
/// Finds all messages matching the specified filter.
diff --git a/src/modules/Elsa.Workflows.Runtime/Handlers/ReadWorkflowInboxMessage.cs b/src/modules/Elsa.Workflows.Runtime/Handlers/ReadWorkflowInboxMessage.cs
index 59e115967..ecf6cb080 100644
--- a/src/modules/Elsa.Workflows.Runtime/Handlers/ReadWorkflowInboxMessage.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Handlers/ReadWorkflowInboxMessage.cs
@@ -1,5 +1,7 @@
+using Elsa.Extensions;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Runtime.Contracts;
+using Elsa.Workflows.Runtime.Models;
using Elsa.Workflows.Runtime.Notifications;
namespace Elsa.Workflows.Runtime.Handlers;
@@ -19,11 +21,16 @@ public class ReadWorkflowInboxMessage : INotificationHandler
public async Task HandleAsync(WorkflowInboxMessageReceived notification, CancellationToken cancellationToken)
{
var message = notification.InboxMessage;
- await _workflowInbox.BroadcastAsync(message, cancellationToken);
+ var options = new BroadcastWorkflowInboxMessageOptions
+ {
+ DispatchAsynchronously = notification.Options.DispatchAsynchronously
+ };
+ var result = await _workflowInbox.BroadcastAsync(message, options, cancellationToken);
+ notification.WorkflowExecutionResults.AddRange(result.WorkflowExecutionResults);
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Models/BroadcastWorkflowInboxMessageOptions.cs b/src/modules/Elsa.Workflows.Runtime/Models/BroadcastWorkflowInboxMessageOptions.cs
new file mode 100644
index 000000000..e43cb2e98
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Runtime/Models/BroadcastWorkflowInboxMessageOptions.cs
@@ -0,0 +1,15 @@
+namespace Elsa.Workflows.Runtime.Models;
+
+///
+/// Represents the options for broadcasting a workflow inbox message.
+///
+public class BroadcastWorkflowInboxMessageOptions
+{
+ ///
+ /// Gets or sets a value indicating whether the dispatch should be executed asynchronously.
+ ///
+ ///
+ /// true if the dispatch should be executed asynchronously; otherwise, false.
+ ///
+ public bool DispatchAsynchronously { get; set; } = true;
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
index 075a960e2..823fc7cd4 100644
--- a/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
@@ -1,5 +1,7 @@
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Runtime.Entities;
+using Elsa.Workflows.Runtime.Options;
+using Elsa.Workflows.Runtime.Results;
namespace Elsa.Workflows.Runtime.Notifications;
@@ -7,4 +9,7 @@ namespace Elsa.Workflows.Runtime.Notifications;
/// A notification that is sent when a workflow inbox message is received.
///
/// The inbox message that was received.
-public record WorkflowInboxMessageReceived(WorkflowInboxMessage InboxMessage) : INotification;
\ No newline at end of file
+public record WorkflowInboxMessageReceived(
+ WorkflowInboxMessage InboxMessage,
+ WorkflowInboxMessageDeliveryOptions Options,
+ ICollection WorkflowExecutionResults) : INotification;
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Options/WorkflowInboxMessageDeliveryOptions.cs b/src/modules/Elsa.Workflows.Runtime/Options/WorkflowInboxMessageDeliveryOptions.cs
index 815de54dc..4b874e210 100644
--- a/src/modules/Elsa.Workflows.Runtime/Options/WorkflowInboxMessageDeliveryOptions.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Options/WorkflowInboxMessageDeliveryOptions.cs
@@ -1,7 +1,3 @@
-using Elsa.Mediator;
-using Elsa.Mediator.Contracts;
-using Elsa.Workflows.Runtime.Notifications;
-
namespace Elsa.Workflows.Runtime.Options;
///
@@ -10,7 +6,7 @@ namespace Elsa.Workflows.Runtime.Options;
public class WorkflowInboxMessageDeliveryOptions
{
///
- /// The strategy to use when publishing the notification.
+ /// Whether to dispatch the message to the workflow dispatcher or send immediately.
///
- public IEventPublishingStrategy EventPublishingStrategy { get; set; } = NotificationStrategy.Background;
+ public bool DispatchAsynchronously { get; set; } = true;
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Results/DeliverWorkflowInboxMessageResult.cs b/src/modules/Elsa.Workflows.Runtime/Results/DeliverWorkflowInboxMessageResult.cs
index a05749e6a..5a786261f 100644
--- a/src/modules/Elsa.Workflows.Runtime/Results/DeliverWorkflowInboxMessageResult.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Results/DeliverWorkflowInboxMessageResult.cs
@@ -3,4 +3,4 @@ namespace Elsa.Workflows.Runtime.Results;
///
/// Result of delivering a workflow inbox message.
///
-public record DeliverWorkflowInboxMessageResult;
\ No newline at end of file
+public record DeliverWorkflowInboxMessageResult(ICollection WorkflowExecutionResults);
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs
index 7f2fb4dda..1b9259f69 100644
--- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs
@@ -1,4 +1,5 @@
using Elsa.Common.Contracts;
+using Elsa.Mediator;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Contracts;
using Elsa.Workflows.Runtime.Contracts;
@@ -16,6 +17,7 @@ namespace Elsa.Workflows.Runtime.Services;
public class DefaultWorkflowInbox : IWorkflowInbox
{
private readonly IWorkflowDispatcher _workflowDispatcher;
+ private readonly IWorkflowRuntime _workflowRuntime;
private readonly IWorkflowInboxMessageStore _messageStore;
private readonly INotificationSender _notificationSender;
private readonly ISystemClock _systemClock;
@@ -27,6 +29,7 @@ public class DefaultWorkflowInbox : IWorkflowInbox
///
public DefaultWorkflowInbox(
IWorkflowDispatcher workflowDispatcher,
+ IWorkflowRuntime workflowRuntime,
IWorkflowInboxMessageStore messageStore,
INotificationSender notificationSender,
ISystemClock systemClock,
@@ -34,6 +37,7 @@ public class DefaultWorkflowInbox : IWorkflowInbox
IBookmarkHasher bookmarkHasher)
{
_workflowDispatcher = workflowDispatcher;
+ _workflowRuntime = workflowRuntime;
_messageStore = messageStore;
_notificationSender = notificationSender;
_systemClock = systemClock;
@@ -72,10 +76,9 @@ public class DefaultWorkflowInbox : IWorkflowInbox
await _messageStore.SaveAsync(message, cancellationToken);
// Send a notification.
- var strategy = options.EventPublishingStrategy;
var workflowExecutionResults = new List();
- var notification = new WorkflowInboxMessageReceived(message);
- await _notificationSender.SendAsync(notification, strategy, cancellationToken);
+ var notification = new WorkflowInboxMessageReceived(message, options, workflowExecutionResults);
+ await _notificationSender.SendAsync(notification, NotificationStrategy.Sequential, cancellationToken);
// Return the result.
return new SubmitWorkflowInboxMessageResult(message, workflowExecutionResults);
@@ -84,19 +87,12 @@ public class DefaultWorkflowInbox : IWorkflowInbox
///
public async ValueTask DeliverAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
{
- await ResumeWorkflowsAsync(message, cancellationToken);
- return new DeliverWorkflowInboxMessageResult();
+ await ResumeWorkflowsAsynchronouslyAsync(message, cancellationToken);
+ return new DeliverWorkflowInboxMessageResult(new List());
}
///
- public async ValueTask BroadcastAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
- {
- await TriggerWorkflowsAsync(message, cancellationToken);
-
- return new DeliverWorkflowInboxMessageResult();
- }
-
- private async Task TriggerWorkflowsAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
+ public async ValueTask BroadcastAsync(WorkflowInboxMessage message, BroadcastWorkflowInboxMessageOptions? options, CancellationToken cancellationToken = default)
{
var activityTypeName = message.ActivityTypeName;
var correlationId = message.CorrelationId;
@@ -107,8 +103,28 @@ public class DefaultWorkflowInbox : IWorkflowInbox
if (workflowInstanceId != null)
{
- await ResumeWorkflowsAsync(message, cancellationToken);
- return;
+ if (options?.DispatchAsynchronously == true)
+ {
+ await ResumeWorkflowsAsynchronouslyAsync(message, cancellationToken);
+ return new DeliverWorkflowInboxMessageResult(new List());
+ }
+
+ var results = await ResumeWorkflowsSynchronouslyAsync(message, cancellationToken);
+ return new DeliverWorkflowInboxMessageResult(results.ToList());
+ }
+
+ if (options?.DispatchAsynchronously == false)
+ {
+ var results = await _workflowRuntime.TriggerWorkflowsAsync(activityTypeName, bookmarkPayload, new TriggerWorkflowsOptions
+ {
+ CorrelationId = correlationId,
+ WorkflowInstanceId = workflowInstanceId,
+ ActivityInstanceId = activityInstanceId,
+ Input = input,
+ CancellationTokens = cancellationToken
+ });
+
+ return new DeliverWorkflowInboxMessageResult(results.TriggeredWorkflows);
}
await _workflowDispatcher.DispatchAsync(new DispatchTriggerWorkflowsRequest(activityTypeName, bookmarkPayload)
@@ -118,9 +134,11 @@ public class DefaultWorkflowInbox : IWorkflowInbox
ActivityInstanceId = activityInstanceId,
Input = input
}, cancellationToken);
+
+ return new DeliverWorkflowInboxMessageResult(new List());
}
- private async Task ResumeWorkflowsAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
+ private async Task ResumeWorkflowsAsynchronouslyAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
{
var activityTypeName = message.ActivityTypeName;
var correlationId = message.CorrelationId;
@@ -138,6 +156,25 @@ public class DefaultWorkflowInbox : IWorkflowInbox
}, cancellationToken);
}
+ private async Task> ResumeWorkflowsSynchronouslyAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)
+ {
+ var activityTypeName = message.ActivityTypeName;
+ var correlationId = message.CorrelationId;
+ var workflowInstanceId = message.WorkflowInstanceId;
+ var activityInstanceId = message.ActivityInstanceId;
+ var bookmarkPayload = message.BookmarkPayload;
+ var input = message.Input;
+
+ return await _workflowRuntime.ResumeWorkflowsAsync(activityTypeName, bookmarkPayload, new TriggerWorkflowsOptions
+ {
+ CorrelationId = correlationId,
+ WorkflowInstanceId = workflowInstanceId,
+ ActivityInstanceId = activityInstanceId,
+ Input = input,
+ CancellationTokens = cancellationToken
+ });
+ }
+
///
public async ValueTask> FindManyAsync(WorkflowInboxMessageFilter filter, CancellationToken cancellationToken = default)
{
diff --git a/src/modules/Elsa.Workflows.Runtime/Services/EventPublisher.cs b/src/modules/Elsa.Workflows.Runtime/Services/EventPublisher.cs
index ff6d59e0c..1acd1159f 100644
--- a/src/modules/Elsa.Workflows.Runtime/Services/EventPublisher.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Services/EventPublisher.cs
@@ -31,7 +31,7 @@ public class EventPublisher : IEventPublisher
IDictionary? input = default,
CancellationToken cancellationToken = default)
{
- return await PublishInternalAsync(eventName, NotificationStrategy.Sequential, correlationId, workflowInstanceId, activityInstanceId, input, cancellationToken);
+ return await PublishInternalAsync(eventName, false, correlationId, workflowInstanceId, activityInstanceId, input, cancellationToken);
}
///
@@ -43,12 +43,12 @@ public class EventPublisher : IEventPublisher
IDictionary? input = default,
CancellationToken cancellationToken = default)
{
- await PublishInternalAsync(eventName, NotificationStrategy.FireAndForget, correlationId, workflowInstanceId, activityInstanceId, input, cancellationToken);
+ await PublishInternalAsync(eventName, true, correlationId, workflowInstanceId, activityInstanceId, input, cancellationToken);
}
private async Task> PublishInternalAsync(
string eventName,
- IEventPublishingStrategy publishingStrategy,
+ bool dispatchAsynchronously,
string? correlationId = default,
string? workflowInstanceId = default,
string? activityInstanceId = default,
@@ -59,7 +59,7 @@ public class EventPublisher : IEventPublisher
var message = NewWorkflowInboxMessage.For(eventBookmark, workflowInstanceId, correlationId, activityInstanceId, input);
var options = new WorkflowInboxMessageDeliveryOptions
{
- EventPublishingStrategy = publishingStrategy,
+ DispatchAsynchronously = dispatchAsynchronously
};
var result = await _workflowInbox.SubmitAsync(message, options, cancellationToken);