diff --git a/src/core/Elsa.Core/Activities/For.cs b/src/core/Elsa.Core/Activities/For.cs index 7d96426a8..a120e45f7 100644 --- a/src/core/Elsa.Core/Activities/For.cs +++ b/src/core/Elsa.Core/Activities/For.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; using Elsa.Attributes; +using Elsa.Behaviors; using Elsa.Contracts; using Elsa.Models; -using Elsa.Signals; namespace Elsa.Activities; @@ -20,7 +20,7 @@ public class For : Activity [JsonConstructor] public For() { - OnSignalReceived(OnBreakAsync); + Behaviors.Add(); } public For(int start, int end, ForOperator forOperator = ForOperator.LessThanOrEqual) : this() @@ -85,16 +85,4 @@ public class For : Activity HandleIteration(ownerActivityExecutionContext); return ValueTask.CompletedTask; } - - private async ValueTask OnBreakAsync(BreakSignal signal, SignalContext context) - { - // Prevent bubbling. - context.StopPropagation(); - - // Remove child activity execution contexts. - context.ActivityExecutionContext.RemoveChildren(); - - // Mark this activity as completed. - await context.ActivityExecutionContext.CompleteActivityAsync(); - } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ForEach.cs b/src/core/Elsa.Core/Activities/ForEach.cs index 58024aa2a..d3ec0b44e 100644 --- a/src/core/Elsa.Core/Activities/ForEach.cs +++ b/src/core/Elsa.Core/Activities/ForEach.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; using Elsa.Attributes; +using Elsa.Behaviors; using Elsa.Contracts; using Elsa.Models; -using Elsa.Signals; namespace Elsa.Activities; @@ -13,7 +13,7 @@ public class ForEach : Activity public ForEach() { - OnSignalReceived(OnBreakAsync); + Behaviors.Add(); } /// @@ -67,18 +67,6 @@ public class ForEach : Activity { await HandleIteration(context); } - - private async ValueTask OnBreakAsync(BreakSignal signal, SignalContext context) - { - // Prevent bubbling. - context.StopPropagation(); - - // Remove child activity execution contexts. - context.ActivityExecutionContext.RemoveChildren(); - - // Mark this activity as completed. - await context.ActivityExecutionContext.CompleteActivityAsync(); - } } public class ForEach : ForEach diff --git a/src/core/Elsa.Core/Activities/While.cs b/src/core/Elsa.Core/Activities/While.cs index 4fef0420f..2adcc682d 100644 --- a/src/core/Elsa.Core/Activities/While.cs +++ b/src/core/Elsa.Core/Activities/While.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; using Elsa.Attributes; +using Elsa.Behaviors; using Elsa.Contracts; using Elsa.Models; -using Elsa.Signals; namespace Elsa.Activities; @@ -18,8 +18,7 @@ public class While : Activity public While(IActivity? body = default) { Body = body!; - - OnSignalReceived(OnBreakAsync); + Behaviors.Add(); } public While(Input condition, IActivity? body = default) : this(body) @@ -67,16 +66,4 @@ public class While : Activity else await context.CompleteActivityAsync(); } - - private async ValueTask OnBreakAsync(BreakSignal signal, SignalContext context) - { - // Prevent bubbling. - context.StopPropagation(); - - // Remove child activity execution contexts. - context.ActivityExecutionContext.RemoveChildren(); - - // Mark this activity as completed. - await context.ActivityExecutionContext.CompleteActivityAsync(); - } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Behaviors/BreakBehavior.cs b/src/core/Elsa.Core/Behaviors/BreakBehavior.cs new file mode 100644 index 000000000..b44d547cc --- /dev/null +++ b/src/core/Elsa.Core/Behaviors/BreakBehavior.cs @@ -0,0 +1,33 @@ +using Elsa.Contracts; +using Elsa.Models; +using Elsa.Signals; + +namespace Elsa.Behaviors; + +/// +/// Implements a "break" behavior that handles the signal. +/// +public class BreakBehavior : IBehavior +{ + public async ValueTask HandleSignalAsync(object signal, SignalContext context) + { + if (signal is not BreakSignal breakSignal) + return; + + await OnBreakAsync(breakSignal, context); + } + + public ValueTask ExecuteAsync(ActivityExecutionContext context) => ValueTask.CompletedTask; + + private async ValueTask OnBreakAsync(BreakSignal signal, SignalContext context) + { + // Prevent bubbling. + context.StopPropagation(); + + // Remove child activity execution contexts. + context.ActivityExecutionContext.RemoveChildren(); + + // Mark this activity as completed. + await context.ActivityExecutionContext.CompleteActivityAsync(); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Contracts/IBehavior.cs b/src/core/Elsa.Core/Contracts/IBehavior.cs new file mode 100644 index 000000000..a3bb23b00 --- /dev/null +++ b/src/core/Elsa.Core/Contracts/IBehavior.cs @@ -0,0 +1,11 @@ +using Elsa.Models; + +namespace Elsa.Contracts; + +public interface IBehavior : ISignalHandler +{ + /// + /// Invoked when the activity executes. + /// + ValueTask ExecuteAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Contracts/ISignalHandler.cs b/src/core/Elsa.Core/Contracts/ISignalHandler.cs index 5adf7df73..ca71b0839 100644 --- a/src/core/Elsa.Core/Contracts/ISignalHandler.cs +++ b/src/core/Elsa.Core/Contracts/ISignalHandler.cs @@ -2,7 +2,7 @@ using Elsa.Models; namespace Elsa.Contracts; -public interface ISignalHandler : IActivity +public interface ISignalHandler { ValueTask HandleSignalAsync(object signal, SignalContext context); } diff --git a/src/core/Elsa.Core/Extensions/BehaviorCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/BehaviorCollectionExtensions.cs new file mode 100644 index 000000000..149a524c8 --- /dev/null +++ b/src/core/Elsa.Core/Extensions/BehaviorCollectionExtensions.cs @@ -0,0 +1,8 @@ +using Elsa.Contracts; + +namespace Elsa; + +public static class BehaviorCollectionExtensions +{ + public static void Add(this ICollection behaviors) where T : IBehavior => behaviors.Add((T)Activator.CreateInstance(typeof(T))!); +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/Activity.cs b/src/core/Elsa.Core/Models/Activity.cs index fc446f3df..0a39ff4fb 100644 --- a/src/core/Elsa.Core/Models/Activity.cs +++ b/src/core/Elsa.Core/Models/Activity.cs @@ -5,9 +5,10 @@ using Elsa.Signals; namespace Elsa.Models; -public abstract class Activity : ISignalHandler +public abstract class Activity : IActivity, ISignalHandler { private readonly ICollection _signalHandlers = new List(); + protected Activity() { TypeName = TypeNameHelper.GenerateTypeName(GetType()); @@ -25,6 +26,8 @@ public abstract class Activity : ISignalHandler public IDictionary ApplicationProperties { get; set; } = new Dictionary(); public IDictionary Metadata { get; set; } = new Dictionary(); + public ICollection Behaviors { get; } = new List(); + /// /// A value indicating whether this activity should complete automatically. /// Default is true. @@ -89,6 +92,9 @@ public abstract class Activity : ISignalHandler async ValueTask IActivity.ExecuteAsync(ActivityExecutionContext context) { await ExecuteAsync(context); + + // Invoke behaviors. + foreach (var behavior in Behaviors) await behavior.ExecuteAsync(context); } async ValueTask ISignalHandler.HandleSignalAsync(object signal, SignalContext context) @@ -102,6 +108,9 @@ public abstract class Activity : ISignalHandler foreach (var registration in handlers) await registration.Handler(signal, context); + + // Invoke behaviors. + foreach (var behavior in Behaviors) await behavior.HandleSignalAsync(signal, context); } }