Add ability to push variables to scheduled work (#4306)

This commit is contained in:
Sipke Schoorstra 2023-08-09 22:35:44 +02:00 committed by GitHub
parent 594c98e066
commit 62ae3b1f6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 172 additions and 100 deletions

View file

@ -1,3 +1,5 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
namespace Elsa.Api.Client.Resources.WorkflowInstances.Models;
/// <summary>
@ -34,4 +36,9 @@ public class ActivityExecutionContextState
/// The evaluated values of the activity's properties.
/// </summary>
public IDictionary<string, object>? ActivityState { get; set; }
/// <summary>
/// A list of dynamically created variables.
/// </summary>
public ICollection<Variable> DynamicVariables { get; set; } = new List<Variable>();
}

View file

@ -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;

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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<T> : Activity
/// <summary>
/// The items to iterate.
/// </summary>
[Input(Description = "The items to iterate.")]
[Input(Description = "The items to iterate through.")]
public Input<ICollection<T>> Items { get; set; } = new(Array.Empty<T>());
/// <summary>
@ -37,17 +39,27 @@ public class ParallelForEach<T> : Activity
/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var items = context.Get(Items)!.Reverse().ToList();
var items = context.Get(Items)!.ToList();
var tags = new List<Guid>();
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<T>("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<T>>
{
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<T> : Activity
private async ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext)
{
var scheduledTags = context.GetProperty<List<Guid>>(ScheduledTagsProperty)!;
var completedTag = (Guid)childContext.Tag!;
var completedTag = childContext.Tag.ConvertTo<Guid>();
var completedTags = new HashSet<Guid>(context.UpdateProperty<List<Guid>>(CompletedTagsProperty, completedTags =>
{

View file

@ -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; }
/// <inheritdoc />
public IEnumerable<Variable> Variables => (Activity as IVariableContainer)?.Variables ?? Enumerable.Empty<Variable>();
public IEnumerable<Variable> Variables
{
get
{
var containerVariables = (Activity as IVariableContainer)?.Variables ?? Enumerable.Empty<Variable>();
var dynamicVariables = DynamicVariables;
return containerVariables.Concat(dynamicVariables).DistinctBy(x => x.Name);
}
}
/// <summary>
/// A list of variables that are dynamically added to the activity execution context.
/// </summary>
public ICollection<Variable> DynamicVariables { get; set; } = new List<Variable>();
/// <summary>
/// The currently executing activity.
@ -159,47 +173,49 @@ public class ActivityExecutionContext : IExecutionContext
/// Schedules the specified activity to be executed.
/// </summary>
/// <param name="activity">The activity to schedule.</param>
/// <param name="completionCallback">An optional callback that is invoked when the activity completes.</param>
/// <param name="tag">An optional tag that can be used to identify the activity.</param>
public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityCompletionCallback? completionCallback = default, object? tag = default)
/// <param name="completionCallback">An optional callback to invoke when the activity completes.</param>
/// <param name="tag">An optional tag to associate with the activity execution.</param>
/// <param name="variables">An optional list of variables to declare with the activity execution.</param>
public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityCompletionCallback? completionCallback, object? tag = default, IEnumerable<Variable>? 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);
}
/// <summary>
/// Schedules the specified activity to be executed.
/// </summary>
/// <param name="activity">The activity to schedule.</param>
/// <param name="owner">The activity execution context that owns the activity.</param>
/// <param name="completionCallback">An optional callback that is invoked when the activity completes.</param>
/// <param name="tag">An optional tag that can be used to identify the activity.</param>
public async ValueTask ScheduleActivityAsync(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, object? tag = default)
/// <param name="options">The options used to schedule the activity.</param>
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);
}
/// <summary>
/// Schedules the specified activity to be executed.
/// </summary>
/// <param name="activityNode">The activity node to schedule.</param>
/// <param name="completionCallback">An optional callback that is invoked when the activity completes.</param>
/// <param name="tag">An optional tag that can be used to identify the activity.</param>
public async ValueTask ScheduleActivityAsync(ActivityNode? activityNode, ActivityCompletionCallback? completionCallback = default, object? tag = default)
/// <param name="activity">The activity to schedule.</param>
/// <param name="owner">The activity execution context that owns the scheduled activity.</param>
/// <param name="options">The options used to schedule the activity.</param>
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);
}
/// <summary>
/// Schedules the specified activity to be executed.
/// </summary>
/// <param name="activityNode">The activity node to schedule.</param>
/// <param name="owner">The activity execution context that owns the activity.</param>
/// <param name="completionCallback">An optional callback that is invoked when the activity completes.</param>
/// <param name="tag">An optional tag that can be used to identify the activity.</param>
public async ValueTask ScheduleActivityAsync(ActivityNode? activityNode, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, object? tag = default)
/// <param name="owner">The activity execution context that owns the scheduled activity.</param>
/// <param name="options">The options used to schedule the activity.</param>
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);
}
/// <summary>
@ -222,11 +238,24 @@ public class ActivityExecutionContext : IExecutionContext
/// Schedules the specified activities to be executed.
/// </summary>
/// <param name="activities">The activities to schedule.</param>
/// <param name="completionCallback">An optional callback that is invoked for each activity when it completes.</param>
public async ValueTask ScheduleActivities(IEnumerable<IActivity?> activities, ActivityCompletionCallback? completionCallback = default)
/// <param name="completionCallback">The callback to invoke when the activities complete.</param>
/// <param name="tag">An optional tag to associate with the activity execution.</param>
/// <param name="variables">An optional list of variables to declare with the activity execution.</param>
public async ValueTask ScheduleActivities(IEnumerable<IActivity?> activities, ActivityCompletionCallback? completionCallback, object? tag = default, IEnumerable<Variable>? variables = default)
{
var options = new ScheduleWorkOptions(completionCallback, tag, variables);
await ScheduleActivities(activities, options);
}
/// <summary>
/// Schedules the specified activities to be executed.
/// </summary>
/// <param name="activities">The activities to schedule.</param>
/// <param name="options">The options used to schedule the activities.</param>
public async ValueTask ScheduleActivities(IEnumerable<IActivity?> activities, ScheduleWorkOptions? options = default)
{
foreach (var activity in activities)
await ScheduleActivityAsync(activity, completionCallback);
await ScheduleActivityAsync(activity, options);
}
/// <summary>

View file

@ -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.
/// </summary>
public DateTimeOffset CreatedAt { get; set; }
/// <summary>
/// A flattened list of <see cref="ActivityNode"/>s from the <see cref="Graph"/>.
/// </summary>
@ -373,16 +374,34 @@ public class WorkflowExecutionContext : IExecutionContext
/// <summary>
/// Creates a new <see cref="ActivityExecutionContext"/> for the specified activity.
/// </summary>
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<Variable>();
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;
}

View file

@ -1,3 +1,5 @@
using Elsa.Workflows.Core.Options;
namespace Elsa.Workflows.Core.Contracts;
/// <summary>
@ -10,12 +12,8 @@ public interface IActivityInvoker
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="activity">The activity to invoke.</param>
/// <param name="owner">The activity execution context that owns the activity.</param>
/// <param name="tag">An optional tag to associate with the activity execution.</param>
Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext,
IActivity activity,
ActivityExecutionContext? owner = default,
object? tag = default);
/// <param name="options"></param>
Task InvokeAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, ActivityInvocationOptions? options = default);
/// <summary>
/// Invokes the specified activity execution context.

View file

@ -19,7 +19,7 @@ public interface IExecutionContext
ExpressionExecutionContext ExpressionExecutionContext { get; }
/// <summary>
/// Returns variables declared the current execution context.
/// Returns variables declared in the current execution context.
/// </summary>
IEnumerable<Variable> Variables { get; }

View file

@ -27,8 +27,4 @@
<ProjectReference Include="..\Elsa.Expressions\Elsa.Expressions.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Bookmarks\" />
</ItemGroup>
</Project>

View file

@ -230,8 +230,7 @@ public static class ActivityExecutionContextExtensions
}
/// <summary>
/// Schedules the specified activity with the provided callback.
/// If the activity is null, the callback is invoked immediately.
/// Schedules the specified activity.
/// </summary>
public static async Task ScheduleOutcomeAsync(this ActivityExecutionContext context, IActivity? activity, [CallerArgumentExpression("activity")] string portPropertyName = default!)
{

View file

@ -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<T>(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);
}
/// <summary>
/// Creates a named variable in the context.
/// </summary>
public static Variable CreateVariable<T>(this ExpressionExecutionContext context, string name, T? value, Type? storageDriverType = null, Action<MemoryBlock>? 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;
}
/// <summary>
/// Sets the value of a named variable in the context.
/// </summary>
public static Variable SetVariable<T>(this ExpressionExecutionContext context, string name, T? value, Action<MemoryBlock>? 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;
}
/// <summary>
/// Sets the output to the specified value.
/// </summary>
public static void Set(this ExpressionExecutionContext context, Output? output, object? value, Action<MemoryBlock>? 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;
}

View file

@ -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<IActivityInvoker>();
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);
}

View file

@ -0,0 +1,11 @@
using Elsa.Workflows.Core.Memory;
namespace Elsa.Workflows.Core.Options;
/// <summary>
/// Represents options for invoking an activity.
/// </summary>
/// <param name="Owner">The activity execution context that owns this invocation.</param>
/// <param name="Tag">An optional tag that can be used to identify the invocation.</param>
/// <param name="Variables">The variables to declare in the activity execution context that will be created for this invocation.</param>
public record ActivityInvocationOptions(ActivityExecutionContext? Owner, object? Tag, IEnumerable<Variable>? Variables);

View file

@ -0,0 +1,12 @@
using Elsa.Workflows.Core.Memory;
using Elsa.Workflows.Core.Services;
namespace Elsa.Workflows.Core.Options;
/// <summary>
/// Represents options for scheduling a work item.
/// </summary>
/// <param name="CompletionCallback">The callback to invoke when the work item has completed.</param>
/// <param name="Tag">A tag that can be used to identify the work item.</param>
/// <param name="Variables">A collection of variables to declare in the activity execution context that will be created for this work item.</param>
public record ScheduleWorkOptions(ActivityCompletionCallback? CompletionCallback = default, object? Tag = default, IEnumerable<Variable>? Variables = default);

View file

@ -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
}
/// <inheritdoc />
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);

View file

@ -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;
}

View file

@ -1,3 +1,5 @@
using Elsa.Workflows.Core.Memory;
namespace Elsa.Workflows.Core.State;
/// <summary>
@ -43,6 +45,11 @@ public class ActivityExecutionContextState
/// The evaluated values of the activity's properties.
/// </summary>
public IDictionary<string, object>? ActivityState { get; set; }
/// <summary>
/// A list of dynamically created variables.
/// </summary>
public ICollection<Variable> DynamicVariables { get; set; } = new List<Variable>();
/// <summary>
/// The status of the activity.

View file

@ -1,6 +1,5 @@
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Filters;
using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Contracts;

View file

@ -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;

View file

@ -1,5 +1,4 @@
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Filters;

View file

@ -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;

View file

@ -1,6 +1,5 @@
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Results;

View file

@ -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;