diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/ActivityExecutionContextState.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/ActivityExecutionContextState.cs
index e6822074f..64181fab4 100644
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/ActivityExecutionContextState.cs
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/ActivityExecutionContextState.cs
@@ -1,3 +1,5 @@
+using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
+
namespace Elsa.Api.Client.Resources.WorkflowInstances.Models;
///
@@ -34,4 +36,9 @@ public class ActivityExecutionContextState
/// The evaluated values of the activity's properties.
///
public IDictionary? ActivityState { get; set; }
+
+ ///
+ /// A list of dynamically created variables.
+ ///
+ public ICollection DynamicVariables { get; set; } = new List();
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowInboxStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowInboxStore.cs
index 825437c45..54226593c 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowInboxStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperWorkflowInboxStore.cs
@@ -8,7 +8,6 @@ using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Dapper.Modules.Runtime.Stores;
diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230809080332_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230809080332_Initial.cs
index 74c603679..8b4ccb3ce 100644
--- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230809080332_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230809080332_Initial.cs
@@ -1,5 +1,4 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230809080343_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230809080343_Initial.cs
index 7299662ec..c0285330f 100644
--- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230809080343_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230809080343_Initial.cs
@@ -1,5 +1,4 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230809080335_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230809080335_Initial.cs
index eb8491684..9d9b527cd 100644
--- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230809080335_Initial.cs
+++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230809080335_Initial.cs
@@ -1,5 +1,4 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
index 24dec0424..da60e7d3b 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs
@@ -1,7 +1,6 @@
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.State;
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs
index bb1866598..218c60d07 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs
@@ -1,7 +1,6 @@
using Elsa.EntityFrameworkCore.Common;
using Elsa.Workflows.Core.State;
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
using Microsoft.EntityFrameworkCore;
namespace Elsa.EntityFrameworkCore.Modules.Runtime;
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs
index 57d34468f..dea7fe9df 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs
@@ -4,7 +4,6 @@ using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.EntityFrameworkCore.Modules.Runtime;
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
index 7f39f19ad..69edb93db 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
@@ -3,7 +3,6 @@ using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Features;
-using Elsa.Workflows.Runtime.Models;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.EntityFrameworkCore.Modules.Runtime;
diff --git a/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs b/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs
index dcc111a25..d9f3c7164 100644
--- a/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs
+++ b/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs
@@ -127,13 +127,9 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
private static object GetVariableInScope(ExpressionExecutionContext context, string variableName)
{
- var q = from variable in EnumerateVariablesInScope(context)
- where variable.Name == variableName
- where variable.TryGet(context, out _)
- select variable.Get(context);
-
- var value = q.FirstOrDefault();
-
+ var variable = context.GetVariable(variableName);
+ var value = variable?.Get(context);
+
return ConvertIEnumerableToArray(value);
}
diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs
index 278bceab6..6ebf912e3 100644
--- a/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs
+++ b/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs
@@ -1,7 +1,6 @@
using Elsa.MongoDb.Helpers;
using Elsa.Workflows.Core.State;
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MongoDB.Driver;
diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
index 7f820d435..b73942a9f 100644
--- a/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
+++ b/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs
@@ -3,7 +3,6 @@ using Elsa.Features.Services;
using Elsa.MongoDb.Common;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Features;
-using Elsa.Workflows.Runtime.Models;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.MongoDb.Modules.Runtime;
diff --git a/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs b/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs
index 9030adb57..e620e6540 100644
--- a/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs
+++ b/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs
@@ -1,5 +1,4 @@
using Elsa.Extensions;
-using Elsa.Telnyx.Attributes;
using Elsa.Telnyx.Bookmarks;
using Elsa.Telnyx.Client.Models;
using Elsa.Telnyx.Client.Services;
@@ -8,7 +7,6 @@ using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Models;
-using Elsa.Workflows.Runtime.Contracts;
using Refit;
namespace Elsa.Telnyx.Activities;
diff --git a/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs b/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs
index bcd67ef3d..50abfb75e 100644
--- a/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs
+++ b/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs
@@ -9,7 +9,6 @@ using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Models;
-using Elsa.Workflows.Runtime.Contracts;
using JetBrains.Annotations;
using Refit;
diff --git a/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs b/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs
index 30c19a3d3..3bb847f67 100644
--- a/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs
+++ b/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs
@@ -10,7 +10,6 @@ using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Activities.Flowchart.Attributes;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Models;
-using Elsa.Workflows.Runtime.Contracts;
using Refit;
namespace Elsa.Telnyx.Activities;
diff --git a/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs b/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs
index 2fb5c1197..76debca73 100644
--- a/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs
+++ b/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs
@@ -9,7 +9,6 @@ using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Activities.Flowchart.Attributes;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Models;
-using Elsa.Workflows.Runtime.Contracts;
using Refit;
namespace Elsa.Telnyx.Activities;
diff --git a/src/modules/Elsa.Telnyx/Handlers/TriggerAnswerCallActivities.cs b/src/modules/Elsa.Telnyx/Handlers/TriggerAnswerCallActivities.cs
index d4f972e2b..75b7634b7 100644
--- a/src/modules/Elsa.Telnyx/Handlers/TriggerAnswerCallActivities.cs
+++ b/src/modules/Elsa.Telnyx/Handlers/TriggerAnswerCallActivities.cs
@@ -4,7 +4,6 @@ using Elsa.Telnyx.Activities;
using Elsa.Telnyx.Bookmarks;
using Elsa.Telnyx.Events;
using Elsa.Telnyx.Extensions;
-using Elsa.Telnyx.Payloads.Abstract;
using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core.Helpers;
using Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Telnyx/Handlers/TriggerCallAnsweredActivities.cs b/src/modules/Elsa.Telnyx/Handlers/TriggerCallAnsweredActivities.cs
index 4302e1e29..c067355b4 100644
--- a/src/modules/Elsa.Telnyx/Handlers/TriggerCallAnsweredActivities.cs
+++ b/src/modules/Elsa.Telnyx/Handlers/TriggerCallAnsweredActivities.cs
@@ -4,7 +4,6 @@ using Elsa.Telnyx.Activities;
using Elsa.Telnyx.Bookmarks;
using Elsa.Telnyx.Events;
using Elsa.Telnyx.Extensions;
-using Elsa.Telnyx.Payloads.Abstract;
using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core.Helpers;
using Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Telnyx/Handlers/TriggerCallHangupActivities.cs b/src/modules/Elsa.Telnyx/Handlers/TriggerCallHangupActivities.cs
index 1567d0507..f8c213f3d 100644
--- a/src/modules/Elsa.Telnyx/Handlers/TriggerCallHangupActivities.cs
+++ b/src/modules/Elsa.Telnyx/Handlers/TriggerCallHangupActivities.cs
@@ -4,7 +4,6 @@ using Elsa.Telnyx.Activities;
using Elsa.Telnyx.Bookmarks;
using Elsa.Telnyx.Events;
using Elsa.Telnyx.Extensions;
-using Elsa.Telnyx.Payloads.Abstract;
using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core.Helpers;
using Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Telnyx/Handlers/TriggerIncomingCallActivities.cs b/src/modules/Elsa.Telnyx/Handlers/TriggerIncomingCallActivities.cs
index ea06ba253..53acaae05 100644
--- a/src/modules/Elsa.Telnyx/Handlers/TriggerIncomingCallActivities.cs
+++ b/src/modules/Elsa.Telnyx/Handlers/TriggerIncomingCallActivities.cs
@@ -4,7 +4,6 @@ using Elsa.Telnyx.Activities;
using Elsa.Telnyx.Bookmarks;
using Elsa.Telnyx.Events;
using Elsa.Telnyx.Extensions;
-using Elsa.Telnyx.Payloads.Abstract;
using Elsa.Telnyx.Payloads.Call;
using Elsa.Workflows.Core.Helpers;
using Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs b/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs
index b81e1b7ed..75ee922d1 100644
--- a/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs
+++ b/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs
@@ -3,7 +3,6 @@ using System.Runtime.CompilerServices;
using Elsa.Extensions;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Contracts;
-using JetBrains.Annotations;
namespace Elsa.Workflows.Core.Activities;
diff --git a/src/modules/Elsa.Workflows.Core/Activities/ParallelForEachT.cs b/src/modules/Elsa.Workflows.Core/Activities/ParallelForEachT.cs
index 6b12df4a9..ccd795fbe 100644
--- a/src/modules/Elsa.Workflows.Core/Activities/ParallelForEachT.cs
+++ b/src/modules/Elsa.Workflows.Core/Activities/ParallelForEachT.cs
@@ -1,9 +1,11 @@
-using System.ComponentModel;
using System.Runtime.CompilerServices;
+using Elsa.Expressions.Helpers;
using Elsa.Extensions;
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Contracts;
+using Elsa.Workflows.Core.Memory;
using Elsa.Workflows.Core.Models;
+using Elsa.Workflows.Core.Services;
namespace Elsa.Workflows.Core.Activities;
@@ -25,7 +27,7 @@ public class ParallelForEach : Activity
///
/// The items to iterate.
///
- [Input(Description = "The items to iterate.")]
+ [Input(Description = "The items to iterate through.")]
public Input> Items { get; set; } = new(Array.Empty());
///
@@ -37,17 +39,27 @@ public class ParallelForEach : Activity
///
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
- var items = context.Get(Items)!.Reverse().ToList();
+ var items = context.Get(Items)!.ToList();
var tags = new List();
foreach (var item in items)
{
- // TODO: For each item, declare a new block of memory to store the item into.
+ // For each item, declare a new variable the work to be scheduled.
+ var variable = new Variable("CurrentItem", item)
+ {
+ // TODO: This should be configurable, because this won't work for e.g. file streams and other non-serializable types.
+ StorageDriverType = typeof(WorkflowStorageDriver)
+ };
+
+ var variables = new List>
+ {
+ variable
+ };
// Schedule a body of work for each item.
var tag = Guid.NewGuid();
tags.Add(tag);
- await context.ScheduleActivityAsync(Body, OnChildCompleted, tag);
+ await context.ScheduleActivityAsync(Body, OnChildCompleted, tag, variables);
}
context.SetProperty(ScheduledTagsProperty, tags);
@@ -57,7 +69,7 @@ public class ParallelForEach : Activity
private async ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext)
{
var scheduledTags = context.GetProperty>(ScheduledTagsProperty)!;
- var completedTag = (Guid)childContext.Tag!;
+ var completedTag = childContext.Tag.ConvertTo();
var completedTags = new HashSet(context.UpdateProperty>(CompletedTagsProperty, completedTags =>
{
diff --git a/src/modules/Elsa.Workflows.Core/Contexts/ActivityExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Contexts/ActivityExecutionContext.cs
index 98da388df..3e8478a9f 100644
--- a/src/modules/Elsa.Workflows.Core/Contexts/ActivityExecutionContext.cs
+++ b/src/modules/Elsa.Workflows.Core/Contexts/ActivityExecutionContext.cs
@@ -7,6 +7,7 @@ using Elsa.Extensions;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Memory;
using Elsa.Workflows.Core.Models;
+using Elsa.Workflows.Core.Options;
using Elsa.Workflows.Core.Services;
namespace Elsa.Workflows.Core;
@@ -87,7 +88,20 @@ public class ActivityExecutionContext : IExecutionContext
public ExpressionExecutionContext ExpressionExecutionContext { get; }
///
- public IEnumerable Variables => (Activity as IVariableContainer)?.Variables ?? Enumerable.Empty();
+ public IEnumerable Variables
+ {
+ get
+ {
+ var containerVariables = (Activity as IVariableContainer)?.Variables ?? Enumerable.Empty();
+ var dynamicVariables = DynamicVariables;
+ return containerVariables.Concat(dynamicVariables).DistinctBy(x => x.Name);
+ }
+ }
+
+ ///
+ /// A list of variables that are dynamically added to the activity execution context.
+ ///
+ public ICollection DynamicVariables { get; set; } = new List();
///
/// The currently executing activity.
@@ -159,47 +173,49 @@ public class ActivityExecutionContext : IExecutionContext
/// Schedules the specified activity to be executed.
///
/// The activity to schedule.
- /// An optional callback that is invoked when the activity completes.
- /// An optional tag that can be used to identify the activity.
- public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityCompletionCallback? completionCallback = default, object? tag = default)
+ /// An optional callback to invoke when the activity completes.
+ /// An optional tag to associate with the activity execution.
+ /// An optional list of variables to declare with the activity execution.
+ public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityCompletionCallback? completionCallback, object? tag = default, IEnumerable? variables = default)
{
- var activityNode = activity != null ? WorkflowExecutionContext.FindNodeByActivity(activity) : default;
- await ScheduleActivityAsync(activityNode, completionCallback, tag);
+ var options = new ScheduleWorkOptions(completionCallback, tag, variables);
+ await ScheduleActivityAsync(activity, options);
}
///
/// Schedules the specified activity to be executed.
///
/// The activity to schedule.
- /// The activity execution context that owns the activity.
- /// An optional callback that is invoked when the activity completes.
- /// An optional tag that can be used to identify the activity.
- public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, object? tag = default)
+ /// The options used to schedule the activity.
+ public async ValueTask ScheduleActivityAsync(IActivity? activity, ScheduleWorkOptions? options = default)
{
var activityNode = activity != null ? WorkflowExecutionContext.FindNodeByActivity(activity) : default;
- await ScheduleActivityAsync(activityNode, this, completionCallback, tag);
+ await ScheduleActivityAsync(activityNode, this, options);
}
///
/// Schedules the specified activity to be executed.
///
- /// The activity node to schedule.
- /// An optional callback that is invoked when the activity completes.
- /// An optional tag that can be used to identify the activity.
- public async ValueTask ScheduleActivityAsync(ActivityNode? activityNode, ActivityCompletionCallback? completionCallback = default, object? tag = default)
+ /// The activity to schedule.
+ /// The activity execution context that owns the scheduled activity.
+ /// The options used to schedule the activity.
+ public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityExecutionContext? owner, ScheduleWorkOptions? options = default)
{
- await ScheduleActivityAsync(activityNode, this, completionCallback, tag);
+ var activityNode = activity != null ? WorkflowExecutionContext.FindNodeByActivity(activity) : default;
+ await ScheduleActivityAsync(activityNode, owner, options);
}
-
+
///
/// Schedules the specified activity to be executed.
///
/// The activity node to schedule.
- /// The activity execution context that owns the activity.
- /// An optional callback that is invoked when the activity completes.
- /// An optional tag that can be used to identify the activity.
- public async ValueTask ScheduleActivityAsync(ActivityNode? activityNode, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, object? tag = default)
+ /// The activity execution context that owns the scheduled activity.
+ /// The options used to schedule the activity.
+ public async ValueTask ScheduleActivityAsync(ActivityNode? activityNode, ActivityExecutionContext? owner = default, ScheduleWorkOptions? options = default)
{
+ var completionCallback = options?.CompletionCallback;
+ owner ??= this;
+
if (activityNode == null)
{
if (completionCallback != null)
@@ -209,7 +225,7 @@ public class ActivityExecutionContext : IExecutionContext
return;
}
- WorkflowExecutionContext.Schedule(activityNode, owner, completionCallback, tag);
+ WorkflowExecutionContext.Schedule(activityNode, owner, options);
}
///
@@ -222,11 +238,24 @@ public class ActivityExecutionContext : IExecutionContext
/// Schedules the specified activities to be executed.
///
/// The activities to schedule.
- /// An optional callback that is invoked for each activity when it completes.
- public async ValueTask ScheduleActivities(IEnumerable activities, ActivityCompletionCallback? completionCallback = default)
+ /// The callback to invoke when the activities complete.
+ /// An optional tag to associate with the activity execution.
+ /// An optional list of variables to declare with the activity execution.
+ public async ValueTask ScheduleActivities(IEnumerable activities, ActivityCompletionCallback? completionCallback, object? tag = default, IEnumerable? variables = default)
+ {
+ var options = new ScheduleWorkOptions(completionCallback, tag, variables);
+ await ScheduleActivities(activities, options);
+ }
+
+ ///
+ /// Schedules the specified activities to be executed.
+ ///
+ /// The activities to schedule.
+ /// The options used to schedule the activities.
+ public async ValueTask ScheduleActivities(IEnumerable activities, ScheduleWorkOptions? options = default)
{
foreach (var activity in activities)
- await ScheduleActivityAsync(activity, completionCallback);
+ await ScheduleActivityAsync(activity, options);
}
///
diff --git a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs
index 402538c64..407edd7bc 100644
--- a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs
+++ b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs
@@ -9,6 +9,7 @@ using Elsa.Common.Contracts;
using Elsa.Workflows.Core.Activities;
using Elsa.Workflows.Core.Memory;
using Elsa.Workflows.Core.Models;
+using Elsa.Workflows.Core.Options;
using JetBrains.Annotations;
namespace Elsa.Workflows.Core;
@@ -122,7 +123,7 @@ public class WorkflowExecutionContext : IExecutionContext
/// The date and time the workflow execution context was created.
///
public DateTimeOffset CreatedAt { get; set; }
-
+
///
/// A flattened list of s from the .
///
@@ -373,16 +374,34 @@ public class WorkflowExecutionContext : IExecutionContext
///
/// Creates a new for the specified activity.
///
- public ActivityExecutionContext CreateActivityExecutionContext(IActivity activity, ActivityExecutionContext? parentContext = default, object? tag = default)
+ public ActivityExecutionContext CreateActivityExecutionContext(IActivity activity, ActivityInvocationOptions? options = default)
{
var activityDescriptor = _activityRegistry.Find(activity) ?? throw new Exception($"Activity with type {activity.Type} not found in registry");
+ var tag = options?.Tag;
+ var parentContext = options?.Owner;
var parentExpressionExecutionContext = parentContext?.ExpressionExecutionContext ?? ExpressionExecutionContext;
var properties = ExpressionExecutionContextExtensions.CreateActivityExecutionContextPropertiesFrom(this, Input);
var memory = new MemoryRegister();
var now = _systemClock.UtcNow;
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, memory, parentExpressionExecutionContext, properties, CancellationToken);
var activityExecutionContext = new ActivityExecutionContext(this, parentContext, expressionExecutionContext, activity, activityDescriptor, now, tag, _systemClock, CancellationToken);
+ var variablesToDeclare = options?.Variables ?? Array.Empty();
+ var variableContainer = new[] { activityExecutionContext.ActivityNode }.Concat(activityExecutionContext.ActivityNode.Ancestors()).FirstOrDefault(x => x.Activity is IVariableContainer)?.Activity as IVariableContainer;
expressionExecutionContext.TransientProperties[ExpressionExecutionContextExtensions.ActivityExecutionContextKey] = activityExecutionContext;
+
+ if (variableContainer != null)
+ {
+ foreach (var variable in variablesToDeclare)
+ {
+ // Declare a dynamic variable on the activity execution context.
+ activityExecutionContext.DynamicVariables.RemoveWhere(x => x.Name == variable.Name);
+ activityExecutionContext.DynamicVariables.Add(variable);
+
+ // Assign the variable to the expression execution context.
+ expressionExecutionContext.CreateVariable(variable.Name, variable.Value);
+ }
+ }
+
return activityExecutionContext;
}
diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IActivityInvoker.cs b/src/modules/Elsa.Workflows.Core/Contracts/IActivityInvoker.cs
index 2b3a18a1c..a12b488ec 100644
--- a/src/modules/Elsa.Workflows.Core/Contracts/IActivityInvoker.cs
+++ b/src/modules/Elsa.Workflows.Core/Contracts/IActivityInvoker.cs
@@ -1,3 +1,5 @@
+using Elsa.Workflows.Core.Options;
+
namespace Elsa.Workflows.Core.Contracts;
///
@@ -10,12 +12,8 @@ public interface IActivityInvoker
///
/// The workflow execution context.
/// The activity to invoke.
- /// The activity execution context that owns the activity.
- /// An optional tag to associate with the activity execution.
- Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext,
- IActivity activity,
- ActivityExecutionContext? owner = default,
- object? tag = default);
+ ///
+ Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, ActivityInvocationOptions? options = default);
///
/// Invokes the specified activity execution context.
diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Contracts/IExecutionContext.cs
index 2566a9d13..249348bac 100644
--- a/src/modules/Elsa.Workflows.Core/Contracts/IExecutionContext.cs
+++ b/src/modules/Elsa.Workflows.Core/Contracts/IExecutionContext.cs
@@ -19,7 +19,7 @@ public interface IExecutionContext
ExpressionExecutionContext ExpressionExecutionContext { get; }
///
- /// Returns variables declared the current execution context.
+ /// Returns variables declared in the current execution context.
///
IEnumerable Variables { get; }
diff --git a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj
index 9a50800f1..59409909c 100644
--- a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj
+++ b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj
@@ -27,8 +27,4 @@
-
-
-
-
diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs
index a604ba050..84ce32023 100644
--- a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs
+++ b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs
@@ -230,8 +230,7 @@ public static class ActivityExecutionContextExtensions
}
///
- /// Schedules the specified activity with the provided callback.
- /// If the activity is null, the callback is invoked immediately.
+ /// Schedules the specified activity.
///
public static async Task ScheduleOutcomeAsync(this ActivityExecutionContext context, IActivity? activity, [CallerArgumentExpression("activity")] string portPropertyName = default!)
{
diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
index e3a003fee..1d39d0806 100644
--- a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
+++ b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
@@ -47,7 +47,7 @@ public static class ExpressionExecutionContextExtensions
public static object? Get(this ExpressionExecutionContext context, Output output) => context.GetBlock(output.MemoryBlockReference).Value;
public static T? GetVariable(this ExpressionExecutionContext context, string name) => (T?)context.GetVariable(name)?.Value;
- private static Variable? GetVariable(this ExpressionExecutionContext context, string name)
+ public static Variable? GetVariable(this ExpressionExecutionContext context, string name)
{
foreach (var block in context.Memory.Blocks.Where(b => b.Value.Metadata is VariableBlockMetadata))
{
@@ -59,9 +59,13 @@ public static class ExpressionExecutionContextExtensions
return context.ParentContext?.GetVariable(name);
}
+ ///
+ /// Creates a named variable in the context.
+ ///
public static Variable CreateVariable(this ExpressionExecutionContext context, string name, T? value, Type? storageDriverType = null, Action? configure = default)
{
var existingVariable = context.GetVariable(name);
+
if(existingVariable != null)
throw new Exception($"Variable {name} already exists in the context.");
@@ -74,17 +78,24 @@ public static class ExpressionExecutionContextExtensions
return variable;
}
+ ///
+ /// Sets the value of a named variable in the context.
+ ///
public static Variable SetVariable(this ExpressionExecutionContext context, string name, T? value, Action? configure = default)
{
var variable = context.GetVariable(name);
- if(variable is null)
- throw new Exception($"Variable {name} not found in the context.");
+
+ if(variable == null)
+ return CreateVariable(context, name, value, configure: configure);
variable.Value = value;
variable.Set(context, value, configure);
return variable;
}
+ ///
+ /// Sets the output to the specified value.
+ ///
public static void Set(this ExpressionExecutionContext context, Output? output, object? value, Action? configure = default)
{
if (output != null) context.Set(output.MemoryBlockReference(), value, configure);
@@ -107,11 +118,9 @@ public static class ExpressionExecutionContextExtensions
while (currentContext != null)
{
var register = currentContext.Memory;
- foreach (var entry in register.Blocks)
- {
- if (!memoryBlocks.ContainsKey(entry.Key))
- memoryBlocks.Add(entry.Key, entry.Value);
- }
+
+ foreach (var entry in register.Blocks)
+ memoryBlocks.TryAdd(entry.Key, entry.Value);
currentContext = currentContext.ParentContext;
}
diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs
index e89b73e0c..180154af8 100644
--- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs
+++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs
@@ -1,7 +1,7 @@
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Models;
-using Elsa.Workflows.Core.Services;
+using Elsa.Workflows.Core.Options;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
@@ -98,13 +98,15 @@ public static class WorkflowExecutionContextExtensions
this WorkflowExecutionContext workflowExecutionContext,
ActivityNode activityNode,
ActivityExecutionContext owner,
- ActivityCompletionCallback? completionCallback = default,
- object? tag = default)
+ ScheduleWorkOptions? options = default)
{
var activityInvoker = workflowExecutionContext.GetRequiredService();
var toolVersion = workflowExecutionContext.Workflow.ToolVersion;
var activityId = toolVersion.Major >= 3 ? activityNode.Activity.Id : activityNode.NodeId;
- var workItem = new ActivityWorkItem(activityId, owner.Id, async () => await activityInvoker.InvokeAsync(workflowExecutionContext, activityNode.Activity, owner, tag), tag);
+ var tag = options?.Tag;
+ var activityInvocationOptions = new ActivityInvocationOptions(owner, tag, options?.Variables);
+ var workItem = new ActivityWorkItem(activityId, owner.Id, async () => await activityInvoker.InvokeAsync(workflowExecutionContext, activityNode.Activity, activityInvocationOptions), tag);
+ var completionCallback = options?.CompletionCallback;
workflowExecutionContext.Scheduler.Schedule(workItem);
workflowExecutionContext.AddCompletionCallback(owner, activityNode, completionCallback);
}
diff --git a/src/modules/Elsa.Workflows.Core/Options/ActivityInvocationOptions.cs b/src/modules/Elsa.Workflows.Core/Options/ActivityInvocationOptions.cs
new file mode 100644
index 000000000..1fbb3479a
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Core/Options/ActivityInvocationOptions.cs
@@ -0,0 +1,11 @@
+using Elsa.Workflows.Core.Memory;
+
+namespace Elsa.Workflows.Core.Options;
+
+///
+/// Represents options for invoking an activity.
+///
+/// The activity execution context that owns this invocation.
+/// An optional tag that can be used to identify the invocation.
+/// The variables to declare in the activity execution context that will be created for this invocation.
+public record ActivityInvocationOptions(ActivityExecutionContext? Owner, object? Tag, IEnumerable? Variables);
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Core/Options/ScheduleWorkOptions.cs b/src/modules/Elsa.Workflows.Core/Options/ScheduleWorkOptions.cs
new file mode 100644
index 000000000..00ecec97b
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Core/Options/ScheduleWorkOptions.cs
@@ -0,0 +1,12 @@
+using Elsa.Workflows.Core.Memory;
+using Elsa.Workflows.Core.Services;
+
+namespace Elsa.Workflows.Core.Options;
+
+///
+/// Represents options for scheduling a work item.
+///
+/// The callback to invoke when the work item has completed.
+/// A tag that can be used to identify the work item.
+/// A collection of variables to declare in the activity execution context that will be created for this work item.
+public record ScheduleWorkOptions(ActivityCompletionCallback? CompletionCallback = default, object? Tag = default, IEnumerable? Variables = default);
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs b/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs
index 9ba6d2576..b6d4347bc 100644
--- a/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs
+++ b/src/modules/Elsa.Workflows.Core/Services/ActivityInvoker.cs
@@ -1,4 +1,5 @@
using Elsa.Workflows.Core.Contracts;
+using Elsa.Workflows.Core.Options;
namespace Elsa.Workflows.Core.Services;
@@ -16,13 +17,10 @@ public class ActivityInvoker : IActivityInvoker
}
///
- public async Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext,
- IActivity activity,
- ActivityExecutionContext? owner,
- object? tag)
+ public async Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, ActivityInvocationOptions? options = default)
{
// Setup an activity execution context.
- var activityExecutionContext = workflowExecutionContext.CreateActivityExecutionContext(activity, owner, tag);
+ var activityExecutionContext = workflowExecutionContext.CreateActivityExecutionContext(activity, options);
// Add the activity context to the workflow context.
workflowExecutionContext.AddActivityExecutionContext(activityExecutionContext);
diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowExecutionContextMapper.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowExecutionContextMapper.cs
index eca422cb1..02389a718 100644
--- a/src/modules/Elsa.Workflows.Core/Services/WorkflowExecutionContextMapper.cs
+++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowExecutionContextMapper.cs
@@ -112,7 +112,9 @@ public class WorkflowExecutionContextMapper : IWorkflowExecutionContextMapper
Status = activityExecutionContext.Status,
StartedAt = activityExecutionContext.StartedAt,
CompletedAt = activityExecutionContext.CompletedAt,
- Tag = activityExecutionContext.Tag
+ Tag = activityExecutionContext.Tag,
+ DynamicVariables = activityExecutionContext.DynamicVariables
+
};
return activityExecutionContextState;
}
@@ -135,6 +137,7 @@ public class WorkflowExecutionContextMapper : IWorkflowExecutionContextMapper
activityExecutionContext.StartedAt = activityExecutionContextState.StartedAt;
activityExecutionContext.CompletedAt = activityExecutionContextState.CompletedAt;
activityExecutionContext.Tag = activityExecutionContextState.Tag;
+ activityExecutionContext.DynamicVariables = activityExecutionContextState.DynamicVariables;
return activityExecutionContext;
}
diff --git a/src/modules/Elsa.Workflows.Core/State/ActivityExecutionContextState.cs b/src/modules/Elsa.Workflows.Core/State/ActivityExecutionContextState.cs
index 0adad9e03..32ab6bd72 100644
--- a/src/modules/Elsa.Workflows.Core/State/ActivityExecutionContextState.cs
+++ b/src/modules/Elsa.Workflows.Core/State/ActivityExecutionContextState.cs
@@ -1,3 +1,5 @@
+using Elsa.Workflows.Core.Memory;
+
namespace Elsa.Workflows.Core.State;
///
@@ -43,6 +45,11 @@ public class ActivityExecutionContextState
/// The evaluated values of the activity's properties.
///
public IDictionary? ActivityState { get; set; }
+
+ ///
+ /// A list of dynamically created variables.
+ ///
+ public ICollection DynamicVariables { get; set; } = new List();
///
/// The status of the activity.
diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInboxStore.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInboxStore.cs
index fbbfdaaee..6aeaa1615 100644
--- a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInboxStore.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowInboxStore.cs
@@ -1,6 +1,5 @@
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Contracts;
diff --git a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
index c2c8177f3..173eb2476 100644
--- a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
@@ -14,7 +14,6 @@ using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Handlers;
using Elsa.Workflows.Runtime.HostedServices;
-using Elsa.Workflows.Runtime.Models;
using Elsa.Workflows.Runtime.Options;
using Elsa.Workflows.Runtime.Providers;
using Elsa.Workflows.Runtime.Services;
diff --git a/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowInboxMessageFilter.cs b/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowInboxMessageFilter.cs
index 28d8aa9eb..5ddc8617a 100644
--- a/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowInboxMessageFilter.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowInboxMessageFilter.cs
@@ -1,5 +1,4 @@
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Filters;
diff --git a/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
index 94149677c..f8a9f22e9 100644
--- a/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowInboxMessageReceived.cs
@@ -1,7 +1,6 @@
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Notifications;
diff --git a/src/modules/Elsa.Workflows.Runtime/Results/SubmitWorkflowInboxMessageResult.cs b/src/modules/Elsa.Workflows.Runtime/Results/SubmitWorkflowInboxMessageResult.cs
index bde41c003..0c28a4694 100644
--- a/src/modules/Elsa.Workflows.Runtime/Results/SubmitWorkflowInboxMessageResult.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Results/SubmitWorkflowInboxMessageResult.cs
@@ -1,6 +1,5 @@
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Results;
diff --git a/src/modules/Elsa.Workflows.Runtime/Stores/MemoryWorkflowInboxStore.cs b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryWorkflowInboxStore.cs
index e80ec6805..cde5839db 100644
--- a/src/modules/Elsa.Workflows.Runtime/Stores/MemoryWorkflowInboxStore.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryWorkflowInboxStore.cs
@@ -2,7 +2,6 @@ using Elsa.Common.Services;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
-using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Stores;