Change For/While/ForEach/IfElse behavior

Before this change, the aforementioned activities would always schedule the Done outcome.
After this change, the Done outcome is scheduled **after** the Iterate branch and True or False branches have completed
This commit is contained in:
Sipke Schoorstra 2021-01-12 09:28:48 +01:00
parent 997fb53b08
commit fcfe7f08b4
28 changed files with 190 additions and 75 deletions

View file

@ -8,12 +8,12 @@ namespace Elsa.Activities.Console
{ {
public static class WriteLineBuilderExtensions public static class WriteLineBuilderExtensions
{ {
public static IOutcomeBuilder WriteLine(this IBuilder builder, Action<ISetupActivity<WriteLine>> setup, string? name = default, string? id = default) => WriteLine(builder.Then(setup).WithName(name).WithId(id)); public static IActivityBuilder WriteLine(this IBuilder builder, Action<ISetupActivity<WriteLine>> setup) => builder.Then(setup);
public static IOutcomeBuilder WriteLine(this IBuilder builder, Func<ActivityExecutionContext, string> text, string? name = default, string? id = default) => builder.WriteLine(activity => activity.Set(x => x.Text, text), name, id); public static IActivityBuilder WriteLine(this IBuilder builder, Func<ActivityExecutionContext, string> text) => builder.WriteLine(activity => activity.WithText(text));
public static IOutcomeBuilder WriteLine(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string>> text, string? name = default, string? id = default) => builder.WriteLine(activity => activity.Set(x => x.Text, text!), name, id); public static IActivityBuilder WriteLine(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string>> text) => builder.WriteLine(activity => activity.WithText(text!));
public static IOutcomeBuilder WriteLine(this IBuilder builder, Func<string> text, string? name = default, string? id = default) => builder.WriteLine(activity => activity.Set(x => x.Text, text), name, id); public static IActivityBuilder WriteLine(this IBuilder builder, Func<string> text) => builder.WriteLine(activity => activity.WithText(text!));
public static IOutcomeBuilder WriteLine(this IBuilder builder, Func<ValueTask<string>> text, string? name = default, string? id = default) => builder.WriteLine(activity => activity.Set(x => x.Text, text!), name, id); public static IActivityBuilder WriteLine(this IBuilder builder, Func<ValueTask<string>> text) => builder.WriteLine(activity => activity.WithText(text!));
public static IOutcomeBuilder WriteLine(this IBuilder builder, string text, string? name = default, string? id = default) => builder.WriteLine(activity => activity.Set(x => x.Text, text), name, id); public static IActivityBuilder WriteLine(this IBuilder builder, string text) => builder.WriteLine(activity => activity.WithText(text!));
private static IOutcomeBuilder WriteLine(IActivityBuilder writeLine) => writeLine.When(OutcomeNames.Done);
} }
} }

View file

@ -25,16 +25,40 @@ namespace Elsa.ActivityResults
{ {
var outcomes = activityExecutionContext.Outcomes = Outcomes.ToList(); var outcomes = activityExecutionContext.Outcomes = Outcomes.ToList();
var workflowExecutionContext = activityExecutionContext.WorkflowExecutionContext; var workflowExecutionContext = activityExecutionContext.WorkflowExecutionContext;
var nextConnections = GetNextConnections(workflowExecutionContext, activityExecutionContext.ActivityBlueprint.Id, outcomes).ToList();
var nextActivities = GetNextActivities( var nextActivities =
workflowExecutionContext, (
activityExecutionContext.ActivityBlueprint.Id, from connection in nextConnections
outcomes).ToList(); from activityBlueprint in workflowExecutionContext.WorkflowBlueprint.Activities
where activityBlueprint.Id == connection.Target.Activity.Id
select activityBlueprint.Id
)
.Distinct();
workflowExecutionContext.ScheduleActivities(nextActivities, activityExecutionContext.Output); foreach (var nextConnection in nextConnections)
workflowExecutionContext.ExecutionLog.Add(nextConnection);
workflowExecutionContext.ScheduleActivities(nextActivities, activityExecutionContext.Output);
} }
private IEnumerable<string> GetNextActivities( public static IEnumerable<string> GetNextActivities(
WorkflowExecutionContext workflowContext,
string sourceId,
IEnumerable<string> outcomes)
{
var nextConnections = GetNextConnections(workflowContext, sourceId, outcomes);
var query =
from connection in nextConnections
from activityBlueprint in workflowContext.WorkflowBlueprint.Activities
where activityBlueprint.Id == connection.Target.Activity.Id
select activityBlueprint.Id;
return query.Distinct();
}
public static IEnumerable<IConnection> GetNextConnections(
WorkflowExecutionContext workflowContext, WorkflowExecutionContext workflowContext,
string sourceId, string sourceId,
IEnumerable<string> outcomes) IEnumerable<string> outcomes)
@ -47,12 +71,10 @@ namespace Elsa.ActivityResults
let connectionOutcome = connection.Source.Outcome ?? OutcomeNames.Done let connectionOutcome = connection.Source.Outcome ?? OutcomeNames.Done
let isConnectionOutcome = connectionOutcome.Equals(outcome.outcome, StringComparison.OrdinalIgnoreCase) let isConnectionOutcome = connectionOutcome.Equals(outcome.outcome, StringComparison.OrdinalIgnoreCase)
where connection.Source.Activity.Id == sourceId && isConnectionOutcome where connection.Source.Activity.Id == sourceId && isConnectionOutcome
from activityBlueprint in workflowContext.WorkflowBlueprint.Activities orderby outcome.order
where activityBlueprint.Id == connection.Target.Activity.Id select connection;
orderby outcome.order
select activityBlueprint.Id;
return query.Distinct(); return query;
} }
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Elsa.Services; using Elsa.Services;
using Elsa.Services.Models;
namespace Elsa.Builders namespace Elsa.Builders
{ {
@ -28,5 +29,6 @@ namespace Elsa.Builders
IActivityBuilder LoadWorkflowContext(bool value = true); IActivityBuilder LoadWorkflowContext(bool value = true);
IActivityBuilder SaveWorkflowContext(bool value = true); IActivityBuilder SaveWorkflowContext(bool value = true);
IActivityBuilder PersistWorkflow(bool value = true); IActivityBuilder PersistWorkflow(bool value = true);
IWorkflowBlueprint Build();
} }
} }

View file

@ -0,0 +1,7 @@
namespace Elsa.Services.Models
{
public interface IBranchingActivity
{
void Unwind(ActivityExecutionContext context);
}
}

View file

@ -67,7 +67,7 @@ namespace Elsa.Services.Models
} }
public bool DeleteCompletedInstances => WorkflowBlueprint.DeleteCompletedInstances; public bool DeleteCompletedInstances => WorkflowBlueprint.DeleteCompletedInstances;
public ICollection<string> ExecutionLog => new List<string>(); public IList<IConnection> ExecutionLog { get; } = new List<IConnection>();
public WorkflowStatus Status => WorkflowInstance.WorkflowStatus; public WorkflowStatus Status => WorkflowInstance.WorkflowStatus;
public bool HasBlockingActivities => WorkflowInstance.BlockingActivities.Any(); public bool HasBlockingActivities => WorkflowInstance.BlockingActivities.Any();
public object? WorkflowContext { get; set; } public object? WorkflowContext { get; set; }
@ -135,12 +135,6 @@ namespace Elsa.Services.Models
public void SetWorkflowContext(object? value) => WorkflowContext = value; public void SetWorkflowContext(object? value) => WorkflowContext = value;
public T GetWorkflowContext<T>() => (T) WorkflowContext!; public T GetWorkflowContext<T>() => (T) WorkflowContext!;
public async ValueTask<IEnumerable<RuntimeActivityInstance>> ActivateActivitiesAsync(CancellationToken cancellationToken = default)
{
var activityExecutionContexts = WorkflowBlueprint.Activities.Select(x => new ActivityExecutionContext(ServiceScope, this, x, null, CancellationToken.None));
return await Task.WhenAll(activityExecutionContexts.Select(async x => await x.ActivateActivityAsync(cancellationToken)));
}
/// <summary> /// <summary>
/// Remove empty activity data to save on document size. /// Remove empty activity data to save on document size.
/// </summary> /// </summary>
@ -156,4 +150,6 @@ namespace Elsa.Services.Models
return activityBlueprint != null && activityBlueprint.PersistOutput; return activityBlueprint != null && activityBlueprint.PersistOutput;
} }
} }
public record ExecutionLogEntry(string ActivityId, string Outcome);
} }

View file

@ -1,7 +1,6 @@
using System; using System;
using Elsa.ActivityResults; using Elsa.ActivityResults;
using Elsa.Attributes; using Elsa.Attributes;
using Elsa.Services;
using Elsa.Services.Models; using Elsa.Services.Models;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
@ -12,7 +11,7 @@ namespace Elsa.Activities.ControlFlow
Description = "Iterate between two numbers.", Description = "Iterate between two numbers.",
Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done } Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done }
)] )]
public class For : Activity public class For : IteratingActivity
{ {
[ActivityProperty(Hint = "An expression that evaluates to the starting number.")] [ActivityProperty(Hint = "An expression that evaluates to the starting number.")]
public long Start { get; set; } public long Start { get; set; }

View file

@ -14,7 +14,7 @@ namespace Elsa.Activities.ControlFlow
Description = "Iterate over a collection.", Description = "Iterate over a collection.",
Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done } Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done }
)] )]
public class ForEach : Activity public class ForEach : IteratingActivity
{ {
[ActivityProperty(Hint = "Enter an expression that evaluates to a collection of items to iterate over.")] [ActivityProperty(Hint = "Enter an expression that evaluates to a collection of items to iterate over.")]
public ICollection<object> Items { get; set; } = new Collection<object>(); public ICollection<object> Items { get; set; } = new Collection<object>();
@ -30,7 +30,7 @@ namespace Elsa.Activities.ControlFlow
get => GetState<int?>(); get => GetState<int?>();
set => SetState(value); set => SetState(value);
} }
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{ {
var collection = ItemsCopy; var collection = ItemsCopy;

View file

@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.ActivityResults; using Elsa.ActivityResults;
using Elsa.Attributes; using Elsa.Attributes;
using Elsa.Services; using Elsa.Services;
@ -12,18 +14,58 @@ namespace Elsa.Activities.ControlFlow
Description = "Evaluate a Boolean expression and continue execution depending on the result.", Description = "Evaluate a Boolean expression and continue execution depending on the result.",
Outcomes = new[] { True, False, OutcomeNames.Done } Outcomes = new[] { True, False, OutcomeNames.Done }
)] )]
public class IfElse : Activity public class IfElse : Activity, IBranchingActivity
{ {
public const string True = "True"; public const string True = "True";
public const string False = "False"; public const string False = "False";
[ActivityProperty(Hint = "The condition to evaluate.")] [ActivityProperty(Hint = "The condition to evaluate.")]
public bool Condition { get; set; } public bool Condition { get; set; }
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{ {
var outcome = Condition ? True : False; var outcome = Condition ? True : False;
return Outcomes(OutcomeNames.Done, outcome); return Outcome(outcome);
} }
public void Unwind(ActivityExecutionContext context)
{
var workflowExecutionContext = context.WorkflowExecutionContext;
var workflowBlueprint = workflowExecutionContext.WorkflowBlueprint;
var activityExecutionContext = context;
var currentActivityBlueprint = activityExecutionContext.ActivityBlueprint;
var currentActivityId = currentActivityBlueprint.Id;
// An IfElse activity completed within a burst of execution, which means its outcome (true or false) did not yield child activities to execute (an empty branch).
// Schedule its child activities connected to the "Done" outcome.
if (currentActivityBlueprint.Type == nameof(IfElse))
{
var nextActivities = GetNextActivities(workflowExecutionContext, currentActivityId);
workflowExecutionContext.ScheduleActivities(nextActivities);
}
else
{
// Get all incoming connections.
var inboundConnections = workflowBlueprint.GetInboundConnectionPath(currentActivityId).ToList();
// Filter out those connections who have a source of IfElse.
var query =
from inboundConnection in inboundConnections
let parentActivityBlueprint = inboundConnection.Source.Activity
where inboundConnection.Source.Activity.Type == nameof(IfElse)
select inboundConnection;
var firstMatch = query.FirstOrDefault();
if (firstMatch != null && firstMatch.Source.Outcome != OutcomeNames.Done)
{
var parentActivityBlueprint = firstMatch.Source.Activity;
var nextActivities = OutcomeResult.GetNextActivities(workflowExecutionContext, parentActivityBlueprint.Id, new[] { OutcomeNames.Done }).ToList();
workflowExecutionContext.ScheduleActivities(nextActivities);
}
}
}
private IEnumerable<string> GetNextActivities(WorkflowExecutionContext workflowExecutionContext, string currentActivityId) => OutcomeResult.GetNextActivities(workflowExecutionContext, currentActivityId, new[] { OutcomeNames.Done });
} }
} }

View file

@ -0,0 +1,29 @@
using System.Linq;
using Elsa.Services;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
public abstract class IteratingActivity : Activity, IBranchingActivity
{
public void Unwind(ActivityExecutionContext context)
{
var workflowExecutionContext = context.WorkflowExecutionContext;
var workflowBlueprint = workflowExecutionContext.WorkflowBlueprint;
var currentActivityId = context.ActivityBlueprint.Id;
var inboundConnections = workflowBlueprint.GetInboundConnectionPath(currentActivityId).ToList();
var query =
from inboundConnection in inboundConnections
let parentActivityBlueprint = inboundConnection.Source.Activity
where inboundConnection.Source.Activity.Type == Type
select inboundConnection;
var firstMatch = query.FirstOrDefault();
if(firstMatch != null && firstMatch.Source.Outcome == OutcomeNames.Iterate)
workflowExecutionContext.ScheduleActivity(firstMatch.Source.Activity.Id);
}
}
}

View file

@ -21,12 +21,26 @@ namespace Elsa.Activities.ControlFlow
[ActivityProperty(Hint = "The conditions to evaluate.")] [ActivityProperty(Hint = "The conditions to evaluate.")]
public SwitchMode Mode { get; set; } = SwitchMode.MatchFirst; public SwitchMode Mode { get; set; } = SwitchMode.MatchFirst;
private bool Evaluated
{
get => GetState<bool>();
set => SetState(value);
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{ {
if (Evaluated)
{
Evaluated = false;
return Done();
}
var matches = Cases.Where(x => x.Condition).Select(x => x.Name).ToList(); var matches = Cases.Where(x => x.Condition).Select(x => x.Name).ToList();
var results = Mode == SwitchMode.MatchFirst ? matches.Any() ? new[] { matches.First() } : new string[0] : matches.ToArray(); var results = Mode == SwitchMode.MatchFirst ? matches.Any() ? new[] { matches.First() } : new string[0] : matches.ToArray();
var outcomes = new[] { OutcomeNames.Done }.Concat(results); var outcomes = new[] { OutcomeNames.Done }.Concat(results);
Evaluated = true;
return Outcomes(outcomes); return Outcomes(outcomes);
} }
} }

View file

@ -11,7 +11,7 @@ namespace Elsa.Activities.ControlFlow
Description = "Execute while a given condition is true.", Description = "Execute while a given condition is true.",
Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done } Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done }
)] )]
public class While : Activity public class While : IteratingActivity
{ {
[ActivityProperty(Hint = "The condition to evaluate.")] [ActivityProperty(Hint = "The condition to evaluate.")]
public bool Condition { get; set; } public bool Condition { get; set; }

View file

@ -25,7 +25,7 @@ namespace Elsa.ActivityResults
if (previousEntry == null) if (previousEntry == null)
return; return;
var activityId = previousEntry; var activityId = previousEntry.Source.Activity.Id;
workflowExecutionContext.ScheduleActivity(activityId, _input); workflowExecutionContext.ScheduleActivity(activityId, _input);
} }
} }

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Elsa.Services; using Elsa.Services;
using Elsa.Services.Models;
namespace Elsa.Builders namespace Elsa.Builders
{ {
@ -108,5 +109,7 @@ namespace Elsa.Builders
PersistWorkflowEnabled = value; PersistWorkflowEnabled = value;
return this; return this;
} }
public IWorkflowBlueprint Build() => ((IWorkflowBuilder)WorkflowBuilder).BuildBlueprint();
} }
} }

View file

@ -3,6 +3,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfinish/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfinish/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfor/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cifthen/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cifthen/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cswitch/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cprimitives_005Csetname/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cprimitives_005Csetname/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Creceivesignal/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Creceivesignal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Csignalreceived/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Csignalreceived/@EntryIndexedValue">True</s:Boolean>

View file

@ -21,6 +21,7 @@ using Elsa.Runtime;
using Elsa.Serialization; using Elsa.Serialization;
using Elsa.Serialization.Converters; using Elsa.Serialization.Converters;
using Elsa.Services; using Elsa.Services;
using Elsa.Services.Models;
using Elsa.StartupTasks; using Elsa.StartupTasks;
using Elsa.Triggers; using Elsa.Triggers;
using Elsa.WorkflowProviders; using Elsa.WorkflowProviders;
@ -54,6 +55,11 @@ namespace Microsoft.Extensions.DependencyInjection
.AddSingleton(options.StorageFactory) .AddSingleton(options.StorageFactory)
.AddStartupTask<CreateSubscriptions>(); .AddStartupTask<CreateSubscriptions>();
services
.AddTransient<IBranchingActivity, IfElse>()
.AddTransient<IBranchingActivity, For>()
.AddTransient<IBranchingActivity, While>();
options options
.AddWorkflowsCore() .AddWorkflowsCore()
.AddCoreActivities(); .AddCoreActivities();

View file

@ -1,4 +1,4 @@
using System.Linq; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Elsa.Events; using Elsa.Events;
@ -12,39 +12,32 @@ namespace Elsa.Handlers
/// Walks up the tee of inbound connections along the "Iterate" outcome of the looping construct (While/For/ForEach) and re-schedules the looping activity. /// Walks up the tee of inbound connections along the "Iterate" outcome of the looping construct (While/For/ForEach) and re-schedules the looping activity.
/// Also handles composite activity re-scheduling. /// Also handles composite activity re-scheduling.
/// </summary> /// </summary>
public class RescheduleLoopsAndContainers : INotificationHandler<WorkflowExecutionBurstCompleted> public class RescheduleBranchingActivitiesAndContainers : INotificationHandler<WorkflowExecutionBurstCompleted>
{ {
private readonly IEnumerable<IBranchingActivity> _branchingActivities;
public RescheduleBranchingActivitiesAndContainers(IEnumerable<IBranchingActivity> branchingActivities)
{
_branchingActivities = branchingActivities;
}
public Task Handle(WorkflowExecutionBurstCompleted notification, CancellationToken cancellationToken) public Task Handle(WorkflowExecutionBurstCompleted notification, CancellationToken cancellationToken)
{ {
ScheduleLoops(notification); var workflowExecutionContext = notification.WorkflowExecutionContext;
var activityExecutionContext = notification.ActivityExecutionContext;
foreach (var branchingActivity in _branchingActivities)
{
if (workflowExecutionContext.HasScheduledActivities || workflowExecutionContext.Status != WorkflowStatus.Running)
break;
branchingActivity.Unwind(activityExecutionContext);
}
ScheduleContainers(notification); ScheduleContainers(notification);
return Task.CompletedTask; return Task.CompletedTask;
} }
private static void ScheduleLoops(WorkflowExecutionBurstCompleted notification)
{
var workflowExecutionContext = notification.WorkflowExecutionContext;
// If no suspension has been instructed, re-schedule any post-scheduled activities.
if (workflowExecutionContext.HasScheduledActivities || workflowExecutionContext.Status != WorkflowStatus.Running)
return;
var workflowBlueprint = workflowExecutionContext.WorkflowBlueprint;
var currentActivityId = notification.ActivityExecutionContext.ActivityBlueprint.Id;
var inboundConnections = workflowBlueprint.GetInboundConnectionPath(currentActivityId).ToList();
var query =
from inboundConnection in inboundConnections
let parentActivityBlueprint = inboundConnection.Source.Activity
where inboundConnection.Source.Outcome == OutcomeNames.Iterate
select parentActivityBlueprint;
var firstLoop = query.FirstOrDefault();
if(firstLoop != null)
workflowExecutionContext.ScheduleActivity(firstLoop.Id);
}
private static void ScheduleContainers(WorkflowExecutionBurstCompleted notification) private static void ScheduleContainers(WorkflowExecutionBurstCompleted notification)
{ {
var workflowExecutionContext = notification.WorkflowExecutionContext; var workflowExecutionContext = notification.WorkflowExecutionContext;

View file

@ -18,6 +18,7 @@ namespace Elsa.Services
var activity = _elsaOptions.ActivityFactory.CreateService(type, context.ServiceScope.ServiceProvider); var activity = _elsaOptions.ActivityFactory.CreateService(type, context.ServiceScope.ServiceProvider);
activity.Data = context.GetData(); activity.Data = context.GetData();
activity.Id = context.ActivityId; activity.Id = context.ActivityId;
await context.WorkflowExecutionContext.WorkflowBlueprint.ActivityPropertyProviders.SetActivityPropertiesAsync(activity, context, context.CancellationToken); await context.WorkflowExecutionContext.WorkflowBlueprint.ActivityPropertyProviders.SetActivityPropertiesAsync(activity, context, context.CancellationToken);
return activity; return activity;
} }

View file

@ -290,7 +290,6 @@ namespace Elsa.Services
await _mediator.Publish(new ActivityExecuted(activityExecutionContext), cancellationToken); await _mediator.Publish(new ActivityExecuted(activityExecutionContext), cancellationToken);
await result.ExecuteAsync(activityExecutionContext, cancellationToken); await result.ExecuteAsync(activityExecutionContext, cancellationToken);
workflowExecutionContext.WorkflowInstance.Output = activityExecutionContext.Output; workflowExecutionContext.WorkflowInstance.Output = activityExecutionContext.Output;
workflowExecutionContext.ExecutionLog.Add(activity.Id);
workflowExecutionContext.PruneActivityData(); workflowExecutionContext.PruneActivityData();
activityOperation = Execute; activityOperation = Execute;
workflowExecutionContext.CompletePass(); workflowExecutionContext.CompletePass();

View file

@ -146,7 +146,7 @@ namespace Elsa.Triggers
if (workflowInstanceId != null) if (workflowInstanceId != null)
{ {
descriptors = _descriptors![workflowBlueprint.Id].ToList(); descriptors = _descriptors!.ContainsKey(workflowBlueprint.Id) ? _descriptors[workflowBlueprint.Id].ToList() : new List<TriggerDescriptor>();
descriptors.RemoveAll(x => x.WorkflowInstanceId == workflowInstanceId); descriptors.RemoveAll(x => x.WorkflowInstanceId == workflowInstanceId);
var workflowInstance = await _workflowInstanceStore.FindByIdAsync(workflowInstanceId, cancellationToken); var workflowInstance = await _workflowInstanceStore.FindByIdAsync(workflowInstanceId, cancellationToken);

View file

@ -17,23 +17,24 @@ namespace Elsa.Samples.IfElseConsole
public void Build(IWorkflowBuilder workflow) public void Build(IWorkflowBuilder workflow)
{ {
workflow workflow
.WriteLine("--POND OF HAPPINESS--", "Start") .WriteLine("--POND OF HAPPINESS--")
.WriteLine("Throw some Rupees in and your wishes will surely come true.") .WriteLine("Throw some Rupees in and your wishes will surely come true.")
.WriteLine("Do you want to throw Rupees?") .WriteLine("Do you want to throw Rupees?").WithName("Start")
.ReadLine() .ReadLine()
.IfElse(context => IsYes(context.Input), .IfElse(context => IsYes(context.Input),
ifElse => ifElse =>
{ {
ifElse ifElse
.When(IfElse.True) .When(OutcomeNames.True)
.WriteLine(GetCurse) .WriteLine(GetCurse)
.WriteLine("...") .WriteLine("...")
.Then("Start"); .Then("Start");
ifElse ifElse
.When(IfElse.False) .When(OutcomeNames.False)
.WriteLine("Bye."); .WriteLine("Keep your rupees.");
}); })
.WriteLine("--END--");
} }
private static bool IsYes(object? value) private static bool IsYes(object? value)

View file

@ -17,7 +17,7 @@ namespace Elsa.Samples.SwitchConsole
public void Build(IWorkflowBuilder workflow) public void Build(IWorkflowBuilder workflow)
{ {
workflow workflow
.WriteLine("--Grayscale Calculator--", "Start") .WriteLine("--Grayscale Calculator--").WithName("Start")
.WriteLine("Enter a number between 0 and 100.") .WriteLine("Enter a number between 0 and 100.")
.ReadLine() .ReadLine()
.Switch(cases => .Switch(cases =>

View file

@ -20,12 +20,12 @@ namespace Elsa.Core.IntegrationTests.Workflows
activity => activity.Set(x => x.Branches, new HashSet<string>(new[] { "Branch 1", "Branch 2", "Branch 3" })), activity => activity.Set(x => x.Branches, new HashSet<string>(new[] { "Branch 1", "Branch 2", "Branch 3" })),
fork => fork =>
{ {
fork.When("Branch 1").SignalReceived("Signal1").WriteLine("Branch 1 executed", "WriteLine1").Then("Join"); fork.When("Branch 1").SignalReceived("Signal1").WriteLine("Branch 1 executed").WithName("WriteLine1").Then("Join");
fork.When("Branch 2").SignalReceived("Signal2").WriteLine("Branch 2 executed", "WriteLine2").Then("Join"); fork.When("Branch 2").SignalReceived("Signal2").WriteLine("Branch 2 executed").WithName("WriteLine2").Then("Join");
fork.When("Branch 3").SignalReceived("Signal3").WriteLine("Branch 3 executed", "WriteLine3").Then("Join"); fork.When("Branch 3").SignalReceived("Signal3").WriteLine("Branch 3 executed").WithName("WriteLine3").Then("Join");
}) })
.Add<Join>(join => join.Set(x => x.Mode, _joinMode)).WithName("Join") .Add<Join>(join => join.Set(x => x.Mode, _joinMode)).WithName("Join")
.WriteLine("Finished", "Finished"); .WriteLine("Finished").WithName("Finished");
} }
} }
} }

View file

@ -25,7 +25,7 @@ namespace Elsa.Core.IntegrationTests.Workflows
{ {
@while @while
.When(OutcomeNames.Iterate) .When(OutcomeNames.Iterate)
.WriteLine(context => $"Inside while loop. Counter = {context.GetVariable<int>(CounterVariableName)}", id: "WriteLoopCount") .WriteLine(context => $"Inside while loop. Counter = {context.GetVariable<int>(CounterVariableName)}").WithId("WriteLoopCount")
.SetVariable(CounterVariableName, context => GetCounter(context) + 1); .SetVariable(CounterVariableName, context => GetCounter(context) + 1);
}) })