From 77ef3838656dfb7d43b25c3561eb745d8ef73f86 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 20 Apr 2022 11:41:29 +0200 Subject: [PATCH] Move completion callback logic to base Activity --- src/core/Elsa.Core/Activities/Container.cs | 18 ----------- src/core/Elsa.Core/Activities/ForEach.cs | 18 ++++++----- src/core/Elsa.Core/Models/Activity.cs | 36 ++++++++++++++++++++-- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/core/Elsa.Core/Activities/Container.cs b/src/core/Elsa.Core/Activities/Container.cs index bdefba3ce..1c5573d76 100644 --- a/src/core/Elsa.Core/Activities/Container.cs +++ b/src/core/Elsa.Core/Activities/Container.cs @@ -13,7 +13,6 @@ public abstract class Container : Activity, IContainer { protected Container() { - OnSignalReceived(OnChildActivityCompletedAsync); } protected Container(params IActivity[] activities) : this() @@ -36,23 +35,6 @@ public abstract class Container : Activity, IContainer // Schedule children. await ScheduleChildrenAsync(context); - - // Because we override ExecuteAsync, this activity will not automatically complete. - // Instead, it's up to the derived classes to explicitly complete this activity. - } - - protected virtual async ValueTask OnChildActivityCompletedAsync(ActivityCompleted signal, SignalContext context) - { - var activityExecutionContext = context.ActivityExecutionContext; - var ownerActivity = activityExecutionContext.Activity; - var childActivityExecutionContext = context.SourceActivityExecutionContext; - var childActivity = childActivityExecutionContext.Activity; - var callbackEntry = activityExecutionContext.WorkflowExecutionContext.CompletionCallbacks.FirstOrDefault(x => x.Owner.Activity == ownerActivity && x.Child == childActivity); - - if (callbackEntry == null) - return; - - await callbackEntry.CompletionCallback(activityExecutionContext, childActivityExecutionContext); } protected virtual ValueTask ScheduleChildrenAsync(ActivityExecutionContext context) diff --git a/src/core/Elsa.Core/Activities/ForEach.cs b/src/core/Elsa.Core/Activities/ForEach.cs index 3b3f1012c..821f09a5a 100644 --- a/src/core/Elsa.Core/Activities/ForEach.cs +++ b/src/core/Elsa.Core/Activities/ForEach.cs @@ -31,37 +31,41 @@ public class ForEach : Activity /// public Variable CurrentValue { get; set; } = new(); - protected override void Execute(ActivityExecutionContext context) + protected override bool CompleteImplicitly => false; + + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) { // Declare looping variable. context.ExpressionExecutionContext.Register.Declare(CurrentValue); // Execute first iteration. - HandleIteration(context); + await HandleIteration(context); } - private void HandleIteration(ActivityExecutionContext context) + private async Task HandleIteration(ActivityExecutionContext context) { var currentIndex = context.GetProperty(CurrentIndexProperty); var items = context.Get(Items)!.ToList(); if (currentIndex >= items.Count) + { + await context.CompleteActivityAsync(); return; + } var currentItem = items[currentIndex]; CurrentValue.Set(context, currentItem); if (Body != null) - context.PostActivity(Body, OnChildCompleted); + context.ScheduleActivity(Body, OnChildCompleted); // Increment index. context.UpdateProperty(CurrentIndexProperty, x => x + 1); } - private ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) + private async ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) { - HandleIteration(context); - return ValueTask.CompletedTask; + await HandleIteration(context); } private void OnBreak(BreakSignal signal, SignalContext context) diff --git a/src/core/Elsa.Core/Models/Activity.cs b/src/core/Elsa.Core/Models/Activity.cs index d6af43c7c..0c3f0d384 100644 --- a/src/core/Elsa.Core/Models/Activity.cs +++ b/src/core/Elsa.Core/Models/Activity.cs @@ -8,19 +8,35 @@ namespace Elsa.Models; public abstract class Activity : ISignalHandler { private readonly ICollection _signalHandlers = new List(); - protected Activity() => TypeName = TypeNameHelper.GenerateTypeName(GetType()); - protected Activity(string activityType) => TypeName = activityType; + protected Activity() + { + TypeName = TypeNameHelper.GenerateTypeName(GetType()); + OnSignalReceived(OnChildActivityCompletedAsync); + } + + protected Activity(string activityType) : this() + { + TypeName = activityType; + } public string Id { get; set; } = default!; public string TypeName { get; set; } public bool CanStartWorkflow { get; set; } public IDictionary ApplicationProperties { get; set; } = new Dictionary(); public IDictionary Metadata { get; set; } = new Dictionary(); + + /// + /// A value indicating whether this activity should complete automatically. + /// Default is true. + /// + protected virtual bool CompleteImplicitly => true; protected virtual async ValueTask ExecuteAsync(ActivityExecutionContext context) { Execute(context); - await CompleteAsync(context); + + if(CompleteImplicitly) + await CompleteAsync(context); } protected virtual ValueTask OnSignalReceivedAsync(object signal, SignalContext context) @@ -56,6 +72,20 @@ public abstract class Activity : ISignalHandler return ValueTask.CompletedTask; }); } + + protected virtual async ValueTask OnChildActivityCompletedAsync(ActivityCompleted signal, SignalContext context) + { + var activityExecutionContext = context.ActivityExecutionContext; + var ownerActivity = activityExecutionContext.Activity; + var childActivityExecutionContext = context.SourceActivityExecutionContext; + var childActivity = childActivityExecutionContext.Activity; + var callbackEntry = activityExecutionContext.WorkflowExecutionContext.CompletionCallbacks.FirstOrDefault(x => x.Owner.Activity == ownerActivity && x.Child == childActivity); + + if (callbackEntry == null) + return; + + await callbackEntry.CompletionCallback(activityExecutionContext, childActivityExecutionContext); + } async ValueTask IActivity.ExecuteAsync(ActivityExecutionContext context) {