diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs index c4946f323..b52dea927 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs @@ -66,7 +66,7 @@ public static class ActivityExecutionContextExtensions public static void SetResult(this ActivityExecutionContext context, object? value) { var activity = context.Activity as IActivityWithResult ?? throw new Exception($"Cannot set result on activity {context.Activity.Id} because it does not implement {nameof(IActivityWithResult)}."); - context.Set(activity.Result, value); + context.Set(activity.Result, value, "Result"); } /// @@ -311,15 +311,6 @@ public static class ActivityExecutionContextExtensions public static IEnumerable GetChildren(this ActivityExecutionContext context) => context.WorkflowExecutionContext.ActiveActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context); - // /// - // /// Removes all child objects. - // /// - // public static async Task RemoveChildrenAsync(this ActivityExecutionContext context) - // { - // // Detach child activity execution contexts. - // await context.WorkflowExecutionContext.RemoveActivityExecutionContextsAsync(context.GetChildren()); - // } - /// /// Send a signal up the current hierarchy of ancestors. /// diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExtensions.cs index e977df7b3..b99d50d5e 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExtensions.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using System.Reflection; using Elsa.Expressions.Helpers; using Elsa.Expressions.Models; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Core.Services; @@ -61,16 +62,40 @@ public static class ActivityExtensions /// /// Gets the output with the specified name. /// - /// The activity. - /// The context. + /// The activity to get the output from. + /// The workflow execution context. + /// Name of the output. + /// The output value. + public static object? GetOutput(this IActivity activity, WorkflowExecutionContext context, string? outputName = default) + { + var workflowExecutionContext = context; + var outputRegister = workflowExecutionContext.GetActivityOutputRegister(); + var output = outputRegister.FindOutputByActivityId(activity.Id, outputName); + return output; + } + + /// + /// Gets the output with the specified name. + /// + /// The activity to get the output from. + /// The activity execution context. + /// Name of the output. + /// The output value. + public static object? GetOutput(this IActivity activity, ActivityExecutionContext context, string? outputName = default) + { + return activity.GetOutput(context.WorkflowExecutionContext, outputName); + } + + /// + /// Gets the output with the specified name. + /// + /// The activity to get the output from. + /// The expression execution context. /// Name of the output. /// The output value. public static object? GetOutput(this IActivity activity, ExpressionExecutionContext context, string? outputName = default) { - var workflowExecutionContext = context.GetWorkflowExecutionContext(); - var outputRegister = workflowExecutionContext.GetActivityOutputRegister(); - var output = outputRegister.FindOutputByActivityInstanceId(context.GetActivityExecutionContext().Id, outputName); - return output; + return activity.GetOutput(context.GetWorkflowExecutionContext(), outputName); } /// @@ -81,11 +106,24 @@ public static class ActivityExtensions /// Name of the output. /// The type of the output. /// The output value. - public static T? GetOutput(this IActivity activity, ExpressionExecutionContext context, string outputName) + public static T? GetOutput(this IActivity activity, ActivityExecutionContext context, string outputName) { var outputValue = activity.GetOutput(context, outputName); return outputValue == null ? default! : outputValue.ConvertTo(); } + + /// + /// Gets the output with the specified name. + /// + /// The activity. + /// The context. + /// Name of the output. + /// The type of the output. + /// The output value. + public static T? GetOutput(this IActivity activity, ExpressionExecutionContext context, string outputName) + { + return activity.GetOutput(context.GetActivityExecutionContext(), outputName); + } /// /// Gets the output with the specified name. @@ -96,7 +134,7 @@ public static class ActivityExtensions /// The type of the activity. /// The type of the output. /// The output value. - public static T? GetOutput(this TActivity activity, ExpressionExecutionContext context, Expression> outputExpression) + public static T? GetOutput(this TActivity activity, ActivityExecutionContext context, Expression> outputExpression) { var outputName = outputExpression.GetPropertyName(); return ((IActivity)activity!).GetOutput(context, outputName); @@ -114,7 +152,7 @@ public static class ActivityExtensions var value = GetResult(activity, context); return value == null ? default! : value.ConvertTo(); } - + /// /// Gets the Result output of the specified activity. /// @@ -122,6 +160,17 @@ public static class ActivityExtensions /// The context. /// The output value. public static object? GetResult(this IActivity activity, ExpressionExecutionContext context) + { + return activity.GetResult(context.GetActivityExecutionContext()); + } + + /// + /// Gets the Result output of the specified activity. + /// + /// The activity. + /// The context. + /// The output value. + public static object? GetResult(this IActivity activity, ActivityExecutionContext context) { return activity.GetOutput(context, ActivityOutputRegister.DefaultOutputName); } @@ -132,7 +181,7 @@ public static class ActivityExtensions /// The context. /// The activity. /// The output value. - public static object? GetResult(this ExpressionExecutionContext context, IActivity activity) + public static object? GetResult(this ActivityExecutionContext context, IActivity activity) { return activity.GetOutput(context, ActivityOutputRegister.DefaultOutputName); } @@ -143,7 +192,7 @@ public static class ActivityExtensions /// The context. public static T? GetLastResult(this ExpressionExecutionContext context) { - var value = GetLastResult(context); + var value = GetLastResult(context.GetWorkflowExecutionContext()); return value == null ? default! : value.ConvertTo(); } @@ -151,10 +200,18 @@ public static class ActivityExtensions /// Gets the result of the last activity. /// /// The context. - public static object? GetLastResult(this ExpressionExecutionContext context) + public static object? GetLastResult(this ActivityExecutionContext context) { - var workflowExecutionContext = context.GetWorkflowExecutionContext(); - return workflowExecutionContext.GetLastActivityResult(); + return context.WorkflowExecutionContext.GetLastResult(); + } + + /// + /// Gets the result of the last activity. + /// + /// The context. + public static object? GetLastResult(this WorkflowExecutionContext context) + { + return context.GetLastActivityResult(); } /// diff --git a/src/modules/Elsa.Workflows.Core/Extensions/OutputExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/OutputExtensions.cs index 8ae49b51d..68fc3c46b 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/OutputExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/OutputExtensions.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Elsa.Expressions.Models; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Memory; @@ -19,7 +20,7 @@ public static class OutputExtensions /// /// Sets the output to the specified value. /// - public static void Set(this Output? output, ActivityExecutionContext context, T? value) => context.Set(output, value); + public static void Set(this Output? output, ActivityExecutionContext context, T? value, [CallerArgumentExpression("output")] string? outputName = default) => context.Set(output, value, outputName); /// /// Sets the output to the specified value. diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumActivity.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumActivity.cs new file mode 100644 index 000000000..84ec6c5a5 --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumActivity.cs @@ -0,0 +1,28 @@ +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Memory; +using Elsa.Workflows.Core.Models; + +namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs; + +public class SumActivity : CodeActivity +{ + public SumActivity(Variable a, Variable b) + { + A = new(a); + B = new(b); + } + + public Input A { get; set; } + public Input B { get; set; } + + protected override void Execute(ActivityExecutionContext context) + { + var input1 = A.Get(context); + var input2 = B.Get(context); + var result = input1 + input2; + + // All three work. + Result.Set(context, result); + } +} \ No newline at end of file diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumWorkflow.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumWorkflow.cs new file mode 100644 index 000000000..4b9336896 --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/SumWorkflow.cs @@ -0,0 +1,31 @@ +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Memory; + +namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs; + +public class SumWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder workflow) + { + var a = new Variable(); + var b = new Variable(); + + var sumActivity = new SumActivity(a, b); + + workflow.Root = new Sequence + { + Variables = { a, b }, + Activities = + { + new Inline(context => a.Set(context, 4)), + new Inline(context => b.Set(context, 6)), + sumActivity, + new WriteLine(context => $"The result of {a.Get(context)} and {b.Get(context)} is {sumActivity.GetResult(context)}."), + new WriteLine(context => $"The last result is {context.GetLastResult()}."), + } + }; + } +} \ No newline at end of file diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/Tests.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/Tests.cs new file mode 100644 index 000000000..a82d562ed --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityOutputs/Tests.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Threading.Tasks; +using Elsa.Testing.Shared; +using Elsa.Workflows.Core.Contracts; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs; + +public class Tests +{ + private readonly IWorkflowRunner _workflowRunner; + private readonly CapturingTextWriter _capturingTextWriter = new(); + + public Tests(ITestOutputHelper testOutputHelper) + { + var services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build(); + _workflowRunner = services.GetRequiredService(); + } + + [Fact(DisplayName = "Activity outputs can be accessed from other activities.")] + public async Task Test1() + { + await _workflowRunner.RunAsync(); + var lines = _capturingTextWriter.Lines.ToList(); + Assert.Equal(new[] + { + "The result of 4 and 6 is 10.", + "The last result is 10." + }, lines); + } +} \ No newline at end of file