Introduce Activity Behaviors
Activity Behaviors allow for code reuse between activities. Useful for certain signal handling like Break for example.
This commit is contained in:
parent
a8cf10c7e5
commit
604b5c3e82
|
|
@ -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<BreakSignal>(OnBreakAsync);
|
||||
Behaviors.Add<BreakBehavior>();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BreakSignal>(OnBreakAsync);
|
||||
Behaviors.Add<BreakBehavior>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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<T> : ForEach
|
||||
|
|
|
|||
|
|
@ -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<BreakSignal>(OnBreakAsync);
|
||||
Behaviors.Add<BreakBehavior>();
|
||||
}
|
||||
|
||||
public While(Input<bool> 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();
|
||||
}
|
||||
}
|
||||
33
src/core/Elsa.Core/Behaviors/BreakBehavior.cs
Normal file
33
src/core/Elsa.Core/Behaviors/BreakBehavior.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using Elsa.Contracts;
|
||||
using Elsa.Models;
|
||||
using Elsa.Signals;
|
||||
|
||||
namespace Elsa.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Implements a "break" behavior that handles the <see cref="BreakSignal"/> signal.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
11
src/core/Elsa.Core/Contracts/IBehavior.cs
Normal file
11
src/core/Elsa.Core/Contracts/IBehavior.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Elsa.Models;
|
||||
|
||||
namespace Elsa.Contracts;
|
||||
|
||||
public interface IBehavior : ISignalHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when the activity executes.
|
||||
/// </summary>
|
||||
ValueTask ExecuteAsync(ActivityExecutionContext context);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ using Elsa.Models;
|
|||
|
||||
namespace Elsa.Contracts;
|
||||
|
||||
public interface ISignalHandler : IActivity
|
||||
public interface ISignalHandler
|
||||
{
|
||||
ValueTask HandleSignalAsync(object signal, SignalContext context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
using Elsa.Contracts;
|
||||
|
||||
namespace Elsa;
|
||||
|
||||
public static class BehaviorCollectionExtensions
|
||||
{
|
||||
public static void Add<T>(this ICollection<IBehavior> behaviors) where T : IBehavior => behaviors.Add((T)Activator.CreateInstance(typeof(T))!);
|
||||
}
|
||||
|
|
@ -5,9 +5,10 @@ using Elsa.Signals;
|
|||
|
||||
namespace Elsa.Models;
|
||||
|
||||
public abstract class Activity : ISignalHandler
|
||||
public abstract class Activity : IActivity, ISignalHandler
|
||||
{
|
||||
private readonly ICollection<SignalHandlerRegistration> _signalHandlers = new List<SignalHandlerRegistration>();
|
||||
|
||||
protected Activity()
|
||||
{
|
||||
TypeName = TypeNameHelper.GenerateTypeName(GetType());
|
||||
|
|
@ -25,6 +26,8 @@ public abstract class Activity : ISignalHandler
|
|||
public IDictionary<string, object> ApplicationProperties { get; set; } = new Dictionary<string, object>();
|
||||
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
public ICollection<IBehavior> Behaviors { get; } = new List<IBehavior>();
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue