diff --git a/src/core/Elsa.Core/Activities/Inline.cs b/src/core/Elsa.Core/Activities/Inline.cs index 1572b5124..8dc0a2c34 100644 --- a/src/core/Elsa.Core/Activities/Inline.cs +++ b/src/core/Elsa.Core/Activities/Inline.cs @@ -53,29 +53,31 @@ public class Inline : Activity { private readonly Func> _activity; - public Inline(Func> activity, RegisterLocationReference? output = default) + public Inline(Func> activity, RegisterLocationReference? captureTarget = default) { _activity = activity; - if (output != null) Result = new Output(output); + + if (captureTarget != null) + this.CaptureOutput(captureTarget); } - public Inline(Func> activity, RegisterLocationReference? output = default) : this(_ => activity(), output) + public Inline(Func> activity, RegisterLocationReference? captureTarget = default) : this(_ => activity(), captureTarget) { } - public Inline(Func activity, RegisterLocationReference? output = default) : this(c => + public Inline(Func activity, RegisterLocationReference? captureTarget = default) : this(c => { var result = activity(c); return new ValueTask(result); - }, output) + }, captureTarget) { } - public Inline(Func activity, RegisterLocationReference? output = default) : this(c => + public Inline(Func activity, RegisterLocationReference? captureTarget = default) : this(c => { var result = activity(); return new ValueTask(result); - }, output) + }, captureTarget) { } diff --git a/src/core/Elsa.Core/Expressions/OutputExpression.cs b/src/core/Elsa.Core/Expressions/OutputExpression.cs index 8f0803385..cafc5ac7b 100644 --- a/src/core/Elsa.Core/Expressions/OutputExpression.cs +++ b/src/core/Elsa.Core/Expressions/OutputExpression.cs @@ -17,13 +17,6 @@ public class OutputExpression : IExpression public Output? Output { get; set; } } -public class OutputExpression : OutputExpression -{ - public OutputExpression(Output? output) : base(output) - { - } -} - public class OutputExpressionHandler : IExpressionHandler { public ValueTask EvaluateAsync(IExpression expression, Type returnType, ExpressionExecutionContext context) diff --git a/src/core/Elsa.Core/Extensions/ActivityExtensions.cs b/src/core/Elsa.Core/Extensions/ActivityExtensions.cs index 9f26e4946..7a9832177 100644 --- a/src/core/Elsa.Core/Extensions/ActivityExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ActivityExtensions.cs @@ -37,7 +37,7 @@ public static class ActivityExtensions /// /// Creates an input from the activity's result. /// - public static Input CreateInput(this Activity activity) => activity.Result.CreateInput(); + public static Input CreateInput(this Activity activity) => activity.Result.CreateInput(); public static IEnumerable GetVariables(this IActivity activity) { diff --git a/src/core/Elsa.Core/Extensions/OutputExtensions.cs b/src/core/Elsa.Core/Extensions/OutputExtensions.cs index 581681daf..7b013039d 100644 --- a/src/core/Elsa.Core/Extensions/OutputExtensions.cs +++ b/src/core/Elsa.Core/Extensions/OutputExtensions.cs @@ -7,5 +7,5 @@ public static class OutputExtensions /// /// Creates an input that references the specified output's value. /// - public static Input CreateInput(this Output? output) => new(output); + public static Input CreateInput(this Output output) => new(output); } \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs b/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs new file mode 100644 index 000000000..a468c5afc --- /dev/null +++ b/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Elsa +{ + public static class PropertyAccessorExtensions + { + public static void SetPropertyValue(this T target, Expression> expression, TProperty value) + { + var property = expression.GetProperty(); + + if (property != null) + property.SetValue(target, value, null); + } + + public static TProperty? GetPropertyValue(this T target, Expression> expression) + { + var property = expression.GetProperty(); + return (TProperty?)property?.GetValue(target); + } + + public static PropertyInfo? GetProperty(this Expression> expression) => + expression.Body is MemberExpression memberExpression + ? memberExpression.Member as PropertyInfo + : expression.Body is UnaryExpression unaryExpression + ? unaryExpression.Operand is MemberExpression unaryMemberExpression + ? unaryMemberExpression.Member as PropertyInfo + : default + : default; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/Activity.cs b/src/core/Elsa.Core/Models/Activity.cs index b0ac7dfab..340666adc 100644 --- a/src/core/Elsa.Core/Models/Activity.cs +++ b/src/core/Elsa.Core/Models/Activity.cs @@ -1,3 +1,4 @@ +using System.Linq.Expressions; using Elsa.Contracts; using Elsa.Helpers; @@ -42,18 +43,28 @@ public abstract class ActivityWithResult : Activity { } - public Output? Result { get; set; } + public Output Result { get; } = new(); } public abstract class Activity : ActivityWithResult { - protected Activity() : base() + protected Activity() { } protected Activity(string activityType) : base(activityType) { } +} - public new Output? Result { get; set; } +public static class ActivityWithResultExtensions +{ + public static T CaptureOutput(this T activity, Expression> propertyExpression, RegisterLocationReference locationReference) where T:IActivity + { + var output = activity.GetPropertyValue(propertyExpression)!; + output.Targets.Add(locationReference); + return activity; + } + + public static T CaptureOutput(this T activity, RegisterLocationReference locationReference) where T : ActivityWithResult => activity.CaptureOutput(x => x.Result, locationReference); } \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs index 66a572760..57b4eceee 100644 --- a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs +++ b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs @@ -150,8 +150,8 @@ public class ActivityExecutionContext } public void Set(RegisterLocationReference locationReference, object? value) => ExpressionExecutionContext.Set(locationReference, value); - public void Set(Output? output, object? value) => ExpressionExecutionContext.Set(output, value); - public void Set(Output? output, T value) => ExpressionExecutionContext.Set(output, value); + public void Set(Output output, object? value) => ExpressionExecutionContext.Set(output, value); + public void Set(Output output, T value) => ExpressionExecutionContext.Set(output, value); public async Task EvaluateAsync(Input input) { diff --git a/src/core/Elsa.Core/Models/Argument.cs b/src/core/Elsa.Core/Models/Argument.cs index e156cc291..4fe26702d 100644 --- a/src/core/Elsa.Core/Models/Argument.cs +++ b/src/core/Elsa.Core/Models/Argument.cs @@ -4,12 +4,12 @@ public abstract class Argument { protected Argument(){} - protected Argument(RegisterLocationReference locationReference, Func? valueConverter = default) + protected Argument(RegisterLocationReference locationReference /*, Func? valueConverter = default*/) { LocationReference = locationReference; - ValueConverter = valueConverter; + //ValueConverter = valueConverter; } public RegisterLocationReference LocationReference { get; set; } = default!; - public Func? ValueConverter { get; set; } + //public Func? ValueConverter { get; set; } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs b/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs index b393d60d8..b452cc823 100644 --- a/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs +++ b/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs @@ -34,7 +34,7 @@ public class ExpressionExecutionContext public object Get(RegisterLocationReference locationReference) => GetLocation(locationReference).Value!; public T Get(RegisterLocationReference locationReference) => (T)Get(locationReference); public T? Get(Input? input) => input != null ? (T?)GetLocation(input.LocationReference).Value : default; - public T? Get(Output? output) => output != null ? (T?)GetLocation(output.LocationReference).Value : default; + public T? Get(Output output) => (T?)GetLocation(output.LocationReference).Value; public object? Get(Output? output) => output != null ? GetLocation(output.LocationReference).Value : default; public T? GetVariable(string name) => (T?)GetVariable(name); public T? GetVariable() => (T?)GetVariable(typeof(T).Name); @@ -56,13 +56,12 @@ public class ExpressionExecutionContext location.Value = value; } - public void Set(Output? output, object? value) + public void Set(Output output, object? value) { - if (output?.LocationReference == null) - return; - - var convertedValue = output.ValueConverter?.Invoke(value) ?? value; - Set(output.LocationReference, convertedValue); + //var convertedValue = output.ValueConverter?.Invoke(value) ?? value; + var convertedValue = value; + var targets = new[] { output.LocationReference }.Concat(output.Targets); + foreach (var target in targets) Set(target, convertedValue); } public T GetRequiredService() where T : notnull => _serviceProvider.GetRequiredService(); diff --git a/src/core/Elsa.Core/Models/Input.cs b/src/core/Elsa.Core/Models/Input.cs index da27f5b35..7214e41ab 100644 --- a/src/core/Elsa.Core/Models/Input.cs +++ b/src/core/Elsa.Core/Models/Input.cs @@ -37,15 +37,11 @@ public class Input : Input { } - public Input(Variable variable) : base(new VariableExpression(variable), variable, typeof(T)) + public Input(Variable variable) : base(new VariableExpression(variable), variable, typeof(T)) { } - public Input(Output? output) : base(new OutputExpression(output), output?.LocationReference ?? new Literal(), typeof(T)) - { - } - - public Input(Output? output) : base(new OutputExpression(output), output?.LocationReference ?? new Literal(), typeof(T)) + public Input(Output output) : base(new OutputExpression(output), output.LocationReference, typeof(T)) { } diff --git a/src/core/Elsa.Core/Models/Output.cs b/src/core/Elsa.Core/Models/Output.cs index 4127b8425..133d8effa 100644 --- a/src/core/Elsa.Core/Models/Output.cs +++ b/src/core/Elsa.Core/Models/Output.cs @@ -2,14 +2,25 @@ namespace Elsa.Models; public class Output : Argument { - public Output(RegisterLocationReference locationReference, Func? valueConverter = default) : base(locationReference, valueConverter) + public Output() : base(new Literal()) { } + + public Output(RegisterLocationReference locationReference) : this() + { + Targets.Add(locationReference); + } + + public ICollection Targets { get; } = new List(); } public class Output : Output { - public Output(RegisterLocationReference locationReference, Func? valueConverter = default) : base(locationReference, valueConverter) + public Output() + { + } + + public Output(Variable locationReference) : base(locationReference) { } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/Variable.cs b/src/core/Elsa.Core/Models/Variable.cs index 9a8d9f7f7..42197f2e6 100644 --- a/src/core/Elsa.Core/Models/Variable.cs +++ b/src/core/Elsa.Core/Models/Variable.cs @@ -1,3 +1,5 @@ +using Elsa.Helpers; + namespace Elsa.Models; public class Variable : RegisterLocationReference @@ -36,6 +38,6 @@ public class Variable : Variable { } - public new T? Get(ActivityExecutionContext context) => (T?)base.Get(context); - public new T? Get(ExpressionExecutionContext context) => (T?)base.Get(context); + public new T? Get(ActivityExecutionContext context) => base.Get(context).ConvertTo(); + public new T? Get(ExpressionExecutionContext context) => base.Get(context).ConvertTo(); } \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs b/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs index 76b07c71b..fd1efedc9 100644 --- a/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs +++ b/src/modules/Elsa.Modules.Activities/Activities/Console/ReadLine.cs @@ -10,7 +10,7 @@ public class ReadLine : Activity { } - public ReadLine(Variable variable, Func? valueConverter = default) => Result = new Output(variable, valueConverter); + public ReadLine(RegisterLocationReference captureTarget) => this.CaptureOutput(captureTarget); protected override void Execute(ActivityExecutionContext context) { diff --git a/src/modules/Elsa.Modules.Activities/Activities/Primitives/Event.cs b/src/modules/Elsa.Modules.Activities/Activities/Primitives/Event.cs index d045d188f..98c92e76f 100644 --- a/src/modules/Elsa.Modules.Activities/Activities/Primitives/Event.cs +++ b/src/modules/Elsa.Modules.Activities/Activities/Primitives/Event.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using Elsa.Attributes; using Elsa.Contracts; using Elsa.Models; @@ -5,8 +6,9 @@ using Elsa.Models; namespace Elsa.Modules.Activities.Activities.Primitives; [Activity("Elsa", "Primitives", "Wait for an event to be triggered.")] -public class Event : Activity +public class Event : Activity { + [JsonConstructor] public Event() { } @@ -29,7 +31,6 @@ public class Event : Activity public Event(Input eventName) => EventName = eventName; [Input] public Input EventName { get; set; } = default!; - [Output] public Output? Payload { get; set; } protected override void Execute(ActivityExecutionContext context) { diff --git a/src/modules/Elsa.Modules.AzureServiceBus/Activities/MessageReceived.cs b/src/modules/Elsa.Modules.AzureServiceBus/Activities/MessageReceived.cs index a17673bef..b6b53ce3b 100644 --- a/src/modules/Elsa.Modules.AzureServiceBus/Activities/MessageReceived.cs +++ b/src/modules/Elsa.Modules.AzureServiceBus/Activities/MessageReceived.cs @@ -46,7 +46,7 @@ public class MessageReceived : Trigger /// /// The received transport message. /// - public Output? ReceivedMessage { get; set; } + public Output ReceivedMessage { get; } = new(); /// /// The formatter to use to parse the message. diff --git a/src/modules/Elsa.Modules.JavaScript/Activities/RunJavaScript.cs b/src/modules/Elsa.Modules.JavaScript/Activities/RunJavaScript.cs index 41d79848d..bab7815a6 100644 --- a/src/modules/Elsa.Modules.JavaScript/Activities/RunJavaScript.cs +++ b/src/modules/Elsa.Modules.JavaScript/Activities/RunJavaScript.cs @@ -1,12 +1,14 @@ -using Elsa.Attributes; +using System.Text.Json.Serialization; +using Elsa.Attributes; using Elsa.Models; using Elsa.Scripting.JavaScript.Contracts; namespace Elsa.Modules.JavaScript.Activities; [Activity("Elsa.Scripting.RunJavaScript", "Executes JavaScript code", "Scripting")] -public class RunJavaScript : Activity +public class RunJavaScript : Activity { + [JsonConstructor] public RunJavaScript() { } @@ -17,7 +19,6 @@ public class RunJavaScript : Activity } public Input Script { get; set; } = new(""); - public Output? Result { get; set; } protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) { diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/CompositeActivitiesWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/CompositeActivitiesWorkflow.cs index 842296ddd..0cce5d70e 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/CompositeActivitiesWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/CompositeActivitiesWorkflow.cs @@ -11,7 +11,7 @@ public class CompositeActivitiesWorkflow : IWorkflow { public void Build(IWorkflowDefinitionBuilder workflow) { - var name = new Variable(); + var name = new Variable(); workflow.WithRoot(new Sequence { diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/ReceiveMessageWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/ReceiveMessageWorkflow.cs index 7509cbb05..adc0e649b 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/ReceiveMessageWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/ReceiveMessageWorkflow.cs @@ -11,20 +11,19 @@ public class ReceiveMessageWorkflow : IWorkflow { public void Build(IWorkflowDefinitionBuilder workflow) { - var receivedMessage = new Variable(); + var receivedMessageVariable = new Variable(); workflow.WithRoot(new Sequence { - Variables = { receivedMessage }, + Variables = { receivedMessageVariable }, Activities = { new MessageReceived { CanStartWorkflow = true, QueueOrTopic = new Input("inbox"), - Result = new Output(receivedMessage) - }, - new WriteLine(context => $"Message received: {receivedMessage.Get(context)}") + }.CaptureOutput(receivedMessageVariable), + new WriteLine(context => $"Message received: {receivedMessageVariable.Get(context)}") } }); } diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/RunJavaScriptWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/RunJavaScriptWorkflow.cs index 76852236c..6c595149a 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/RunJavaScriptWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/RunJavaScriptWorkflow.cs @@ -18,10 +18,7 @@ public class RunJavaScriptWorkflow : IWorkflow Variables = { scriptResult }, Activities = { - new RunJavaScript("1 + 1") - { - Result = new Output(scriptResult) - }, + new RunJavaScript("1 + 1").CaptureOutput(scriptResult), new WriteLine(context => $"Result: {scriptResult.Get(context)}") } }); diff --git a/src/samples/console/Elsa.Samples.Console1/Program.cs b/src/samples/console/Elsa.Samples.Console1/Program.cs index fa497e4b7..1de0055ba 100644 --- a/src/samples/console/Elsa.Samples.Console1/Program.cs +++ b/src/samples/console/Elsa.Samples.Console1/Program.cs @@ -50,7 +50,7 @@ class Program var workflow13 = new Func(BlockingParallelForEachWorkflow.Create); var workflow14 = new Func(FlowchartWorkflow.Create); - var workflowFactory = workflow10; + var workflowFactory = workflow3; var workflowGraph = workflowFactory(); var workflow = Workflow.FromActivity(workflowGraph); diff --git a/src/samples/console/Elsa.Samples.Console1/Workflows/ConditionalWorkflow.cs b/src/samples/console/Elsa.Samples.Console1/Workflows/ConditionalWorkflow.cs index 64578ed1f..7e6babab9 100644 --- a/src/samples/console/Elsa.Samples.Console1/Workflows/ConditionalWorkflow.cs +++ b/src/samples/console/Elsa.Samples.Console1/Workflows/ConditionalWorkflow.cs @@ -17,7 +17,7 @@ public static class ConditionalWorkflow Activities = { new WriteLine("What's your age?"), - new ReadLine(age, s => int.Parse((string)s!)), + new ReadLine(age), new If { Condition = new Input(context => age.Get(context) >= 16), diff --git a/src/samples/console/Elsa.Samples.Console1/Workflows/FlowchartWorkflow.cs b/src/samples/console/Elsa.Samples.Console1/Workflows/FlowchartWorkflow.cs index a2133ed19..84b457b2e 100644 --- a/src/samples/console/Elsa.Samples.Console1/Workflows/FlowchartWorkflow.cs +++ b/src/samples/console/Elsa.Samples.Console1/Workflows/FlowchartWorkflow.cs @@ -13,7 +13,7 @@ public static class FlowchartWorkflow var age = new Variable(); var flowchart = new Flowchart(); var step1 = new WriteLine("Please enter your age."); - var step2 = new ReadLine(age, x => int.Parse((string)x!)); + var step2 = new ReadLine(age); var step3 = new If { Condition = new Input(context => age.Get(context) > 16), diff --git a/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs b/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs index 1ba499726..674bcd7ef 100644 --- a/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs +++ b/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs @@ -18,10 +18,7 @@ public static class VariablesWorkflow Activities = { new WriteLine(context => greeting.Get(context)), - new ReadLine - { - Result = new Output(name) - }, + new ReadLine(name), new WriteLine(new DelegateReference(context => $"Nice to meet you, {name.Get(context)}!")), } };