Refactor output capture API
This commit is contained in:
parent
e7c36ed7ff
commit
5d8d77deea
|
|
@ -53,29 +53,31 @@ public class Inline<T> : Activity<T>
|
|||
{
|
||||
private readonly Func<ActivityExecutionContext, ValueTask<T>> _activity;
|
||||
|
||||
public Inline(Func<ActivityExecutionContext, ValueTask<T>> activity, RegisterLocationReference? output = default)
|
||||
public Inline(Func<ActivityExecutionContext, ValueTask<T>> activity, RegisterLocationReference? captureTarget = default)
|
||||
{
|
||||
_activity = activity;
|
||||
if (output != null) Result = new Output<T?>(output);
|
||||
|
||||
if (captureTarget != null)
|
||||
this.CaptureOutput(captureTarget);
|
||||
}
|
||||
|
||||
public Inline(Func<ValueTask<T>> activity, RegisterLocationReference? output = default) : this(_ => activity(), output)
|
||||
public Inline(Func<ValueTask<T>> activity, RegisterLocationReference? captureTarget = default) : this(_ => activity(), captureTarget)
|
||||
{
|
||||
}
|
||||
|
||||
public Inline(Func<ActivityExecutionContext, T> activity, RegisterLocationReference? output = default) : this(c =>
|
||||
public Inline(Func<ActivityExecutionContext, T> activity, RegisterLocationReference? captureTarget = default) : this(c =>
|
||||
{
|
||||
var result = activity(c);
|
||||
return new ValueTask<T>(result);
|
||||
}, output)
|
||||
}, captureTarget)
|
||||
{
|
||||
}
|
||||
|
||||
public Inline(Func<T> activity, RegisterLocationReference? output = default) : this(c =>
|
||||
public Inline(Func<T> activity, RegisterLocationReference? captureTarget = default) : this(c =>
|
||||
{
|
||||
var result = activity();
|
||||
return new ValueTask<T>(result);
|
||||
}, output)
|
||||
}, captureTarget)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,6 @@ public class OutputExpression : IExpression
|
|||
public Output? Output { get; set; }
|
||||
}
|
||||
|
||||
public class OutputExpression<T> : OutputExpression
|
||||
{
|
||||
public OutputExpression(Output<T>? output) : base(output)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class OutputExpressionHandler : IExpressionHandler
|
||||
{
|
||||
public ValueTask<object?> EvaluateAsync(IExpression expression, Type returnType, ExpressionExecutionContext context)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public static class ActivityExtensions
|
|||
/// <summary>
|
||||
/// Creates an input from the activity's result.
|
||||
/// </summary>
|
||||
public static Input<T?> CreateInput<T>(this Activity<T> activity) => activity.Result.CreateInput();
|
||||
public static Input<T?> CreateInput<T>(this Activity<T> activity) => activity.Result.CreateInput<T?>();
|
||||
|
||||
public static IEnumerable<Variable> GetVariables(this IActivity activity)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ public static class OutputExtensions
|
|||
/// <summary>
|
||||
/// Creates an input that references the specified output's value.
|
||||
/// </summary>
|
||||
public static Input<T> CreateInput<T>(this Output<T>? output) => new(output);
|
||||
public static Input<T> CreateInput<T>(this Output output) => new(output);
|
||||
}
|
||||
32
src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs
Normal file
32
src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Elsa
|
||||
{
|
||||
public static class PropertyAccessorExtensions
|
||||
{
|
||||
public static void SetPropertyValue<T, TProperty>(this T target, Expression<Func<T, TProperty>> expression, TProperty value)
|
||||
{
|
||||
var property = expression.GetProperty();
|
||||
|
||||
if (property != null)
|
||||
property.SetValue(target, value, null);
|
||||
}
|
||||
|
||||
public static TProperty? GetPropertyValue<T, TProperty>(this T target, Expression<Func<T, TProperty>> expression)
|
||||
{
|
||||
var property = expression.GetProperty();
|
||||
return (TProperty?)property?.GetValue(target);
|
||||
}
|
||||
|
||||
public static PropertyInfo? GetProperty<T, TProperty>(this Expression<Func<T, TProperty>> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<T> : ActivityWithResult
|
||||
{
|
||||
protected Activity() : base()
|
||||
protected Activity()
|
||||
{
|
||||
}
|
||||
|
||||
protected Activity(string activityType) : base(activityType)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public new Output<T?>? Result { get; set; }
|
||||
public static class ActivityWithResultExtensions
|
||||
{
|
||||
public static T CaptureOutput<T>(this T activity, Expression<Func<T, Output>> propertyExpression, RegisterLocationReference locationReference) where T:IActivity
|
||||
{
|
||||
var output = activity.GetPropertyValue(propertyExpression)!;
|
||||
output.Targets.Add(locationReference);
|
||||
return activity;
|
||||
}
|
||||
|
||||
public static T CaptureOutput<T>(this T activity, RegisterLocationReference locationReference) where T : ActivityWithResult => activity.CaptureOutput(x => x.Result, locationReference);
|
||||
}
|
||||
|
|
@ -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<T>(Output<T>? output, T value) => ExpressionExecutionContext.Set(output, value);
|
||||
public void Set(Output output, object? value) => ExpressionExecutionContext.Set(output, value);
|
||||
public void Set<T>(Output output, T value) => ExpressionExecutionContext.Set(output, value);
|
||||
|
||||
public async Task<T?> EvaluateAsync<T>(Input<T> input)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ public abstract class Argument
|
|||
{
|
||||
protected Argument(){}
|
||||
|
||||
protected Argument(RegisterLocationReference locationReference, Func<object?, object?>? valueConverter = default)
|
||||
protected Argument(RegisterLocationReference locationReference /*, Func<object?, object?>? valueConverter = default*/)
|
||||
{
|
||||
LocationReference = locationReference;
|
||||
ValueConverter = valueConverter;
|
||||
//ValueConverter = valueConverter;
|
||||
}
|
||||
|
||||
public RegisterLocationReference LocationReference { get; set; } = default!;
|
||||
public Func<object?, object?>? ValueConverter { get; set; }
|
||||
//public Func<object?, object?>? ValueConverter { get; set; }
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ public class ExpressionExecutionContext
|
|||
public object Get(RegisterLocationReference locationReference) => GetLocation(locationReference).Value!;
|
||||
public T Get<T>(RegisterLocationReference locationReference) => (T)Get(locationReference);
|
||||
public T? Get<T>(Input<T>? input) => input != null ? (T?)GetLocation(input.LocationReference).Value : default;
|
||||
public T? Get<T>(Output<T>? output) => output != null ? (T?)GetLocation(output.LocationReference).Value : default;
|
||||
public T? Get<T>(Output output) => (T?)GetLocation(output.LocationReference).Value;
|
||||
public object? Get(Output? output) => output != null ? GetLocation(output.LocationReference).Value : default;
|
||||
public T? GetVariable<T>(string name) => (T?)GetVariable(name);
|
||||
public T? GetVariable<T>() => (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<T>() where T : notnull => _serviceProvider.GetRequiredService<T>();
|
||||
|
|
|
|||
|
|
@ -37,15 +37,11 @@ public class Input<T> : Input
|
|||
{
|
||||
}
|
||||
|
||||
public Input(Variable<T> variable) : base(new VariableExpression(variable), variable, typeof(T))
|
||||
public Input(Variable variable) : base(new VariableExpression(variable), variable, typeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
public Input(Output<T>? output) : base(new OutputExpression<T>(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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,25 @@ namespace Elsa.Models;
|
|||
|
||||
public class Output : Argument
|
||||
{
|
||||
public Output(RegisterLocationReference locationReference, Func<object?, object?>? valueConverter = default) : base(locationReference, valueConverter)
|
||||
public Output() : base(new Literal())
|
||||
{
|
||||
}
|
||||
|
||||
public Output(RegisterLocationReference locationReference) : this()
|
||||
{
|
||||
Targets.Add(locationReference);
|
||||
}
|
||||
|
||||
public ICollection<RegisterLocationReference> Targets { get; } = new List<RegisterLocationReference>();
|
||||
}
|
||||
|
||||
public class Output<T> : Output
|
||||
{
|
||||
public Output(RegisterLocationReference locationReference, Func<object?, object?>? valueConverter = default) : base(locationReference, valueConverter)
|
||||
public Output()
|
||||
{
|
||||
}
|
||||
|
||||
public Output(Variable<T> locationReference) : base(locationReference)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using Elsa.Helpers;
|
||||
|
||||
namespace Elsa.Models;
|
||||
|
||||
public class Variable : RegisterLocationReference
|
||||
|
|
@ -36,6 +38,6 @@ public class Variable<T> : 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<T?>();
|
||||
public new T? Get(ExpressionExecutionContext context) => base.Get(context).ConvertTo<T?>();
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ public class ReadLine : Activity<string>
|
|||
{
|
||||
}
|
||||
|
||||
public ReadLine(Variable variable, Func<object?, object?>? valueConverter = default) => Result = new Output<string?>(variable, valueConverter);
|
||||
public ReadLine(RegisterLocationReference captureTarget) => this.CaptureOutput(captureTarget);
|
||||
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<object?>
|
||||
{
|
||||
[JsonConstructor]
|
||||
public Event()
|
||||
{
|
||||
}
|
||||
|
|
@ -29,7 +31,6 @@ public class Event : Activity
|
|||
public Event(Input<string> eventName) => EventName = eventName;
|
||||
|
||||
[Input] public Input<string> EventName { get; set; } = default!;
|
||||
[Output] public Output<object?>? Payload { get; set; }
|
||||
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class MessageReceived : Trigger<object>
|
|||
/// <summary>
|
||||
/// The received transport message.
|
||||
/// </summary>
|
||||
public Output<ReceivedServiceBusMessageModel>? ReceivedMessage { get; set; }
|
||||
public Output<ReceivedServiceBusMessageModel> ReceivedMessage { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The formatter to use to parse the message.
|
||||
|
|
|
|||
|
|
@ -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<object?>
|
||||
{
|
||||
[JsonConstructor]
|
||||
public RunJavaScript()
|
||||
{
|
||||
}
|
||||
|
|
@ -17,7 +19,6 @@ public class RunJavaScript : Activity
|
|||
}
|
||||
|
||||
public Input<string> Script { get; set; } = new("");
|
||||
public Output<object?>? Result { get; set; }
|
||||
|
||||
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public class CompositeActivitiesWorkflow : IWorkflow
|
|||
{
|
||||
public void Build(IWorkflowDefinitionBuilder workflow)
|
||||
{
|
||||
var name = new Variable<string>();
|
||||
var name = new Variable<string?>();
|
||||
|
||||
workflow.WithRoot(new Sequence
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ public class ReceiveMessageWorkflow : IWorkflow
|
|||
{
|
||||
public void Build(IWorkflowDefinitionBuilder workflow)
|
||||
{
|
||||
var receivedMessage = new Variable<string>();
|
||||
var receivedMessageVariable = new Variable<string>();
|
||||
|
||||
workflow.WithRoot(new Sequence
|
||||
{
|
||||
Variables = { receivedMessage },
|
||||
Variables = { receivedMessageVariable },
|
||||
Activities =
|
||||
{
|
||||
new MessageReceived
|
||||
{
|
||||
CanStartWorkflow = true,
|
||||
QueueOrTopic = new Input<string>("inbox"),
|
||||
Result = new Output<object?>(receivedMessage)
|
||||
},
|
||||
new WriteLine(context => $"Message received: {receivedMessage.Get(context)}")
|
||||
}.CaptureOutput(receivedMessageVariable),
|
||||
new WriteLine(context => $"Message received: {receivedMessageVariable.Get(context)}")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ public class RunJavaScriptWorkflow : IWorkflow
|
|||
Variables = { scriptResult },
|
||||
Activities =
|
||||
{
|
||||
new RunJavaScript("1 + 1")
|
||||
{
|
||||
Result = new Output<object?>(scriptResult)
|
||||
},
|
||||
new RunJavaScript("1 + 1").CaptureOutput(scriptResult),
|
||||
new WriteLine(context => $"Result: {scriptResult.Get(context)}")
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class Program
|
|||
var workflow13 = new Func<IActivity>(BlockingParallelForEachWorkflow.Create);
|
||||
var workflow14 = new Func<IActivity>(FlowchartWorkflow.Create);
|
||||
|
||||
var workflowFactory = workflow10;
|
||||
var workflowFactory = workflow3;
|
||||
var workflowGraph = workflowFactory();
|
||||
var workflow = Workflow.FromActivity(workflowGraph);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<bool>(context => age.Get(context) >= 16),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public static class FlowchartWorkflow
|
|||
var age = new Variable<int>();
|
||||
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<bool>(context => age.Get(context) > 16),
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ public static class VariablesWorkflow
|
|||
Activities =
|
||||
{
|
||||
new WriteLine(context => greeting.Get(context)),
|
||||
new ReadLine
|
||||
{
|
||||
Result = new Output<string?>(name)
|
||||
},
|
||||
new ReadLine(name),
|
||||
new WriteLine(new DelegateReference(context => $"Nice to meet you, {name.Get(context)}!")),
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue