Various API enhancements

This commit is contained in:
Sipke Schoorstra 2022-03-09 13:53:34 +01:00
parent 4f3a3de208
commit a9fbb57a09
18 changed files with 196 additions and 37 deletions

View file

@ -3,19 +3,17 @@ using Elsa.Models;
namespace Elsa.Activities.Console;
public class ReadLine : Activity
public class ReadLine : Activity<string>
{
public ReadLine()
{
}
public ReadLine(Variable variable, Func<object?, object?>? valueConverter = default) => Output = new Output<string?>(variable, valueConverter);
[Output] public Output<string?>? Output { get; set; }
public ReadLine(Variable variable, Func<object?, object?>? valueConverter = default) => Result = new Output<string?>(variable, valueConverter);
protected override void Execute(ActivityExecutionContext context)
{
var text = System.Console.ReadLine();
context.Set(Output, text);
context.Set(Result, text);
}
}

View file

@ -55,7 +55,7 @@ public class For : Activity
if (loop)
{
context.ScheduleActivity(iterateNode, OnChildComplete);
context.SubmitActivity(iterateNode, OnChildComplete);
// Update loop variable.
CurrentValue.Set(context.ExpressionExecutionContext, currentValue);

View file

@ -32,7 +32,7 @@ public class ForEach : Activity
CurrentValue.Set(context, currentItem);
if (Body != null)
context.ScheduleActivity(Body, OnChildCompleted);
context.SubmitActivity(Body, OnChildCompleted);
// Increment index.
context.UpdateProperty<int>(CurrentIndexProperty, x => x + 1);

View file

@ -6,6 +6,14 @@ namespace Elsa.Activities.ControlFlow;
public class If : Activity
{
public If()
{
}
public If(Input<bool> condition) => Condition = condition;
public If(Func<ExpressionExecutionContext, bool> condition) => Condition = new Input<bool>(condition);
public If(Func<bool> condition) => Condition = new Input<bool>(condition);
[Input] public Input<bool> Condition { get; set; } = new(new Literal<bool>(false));
[Outbound] public IActivity? Then { get; set; }
[Outbound] public IActivity? Else { get; set; }
@ -16,7 +24,7 @@ public class If : Activity
var nextNode = result ? Then : Else;
if (nextNode != null)
context.ScheduleActivity(nextNode, OnChildCompletedAsync);
context.SubmitActivity(nextNode, OnChildCompletedAsync);
}
private ValueTask OnChildCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext)

View file

@ -26,7 +26,7 @@ public class ParallelForEach<T> : Activity
};
// Schedule a body of work for each item.
context.ScheduleActivity(Body, OnChildCompleted, new[]{localVariable});
context.SubmitActivity(Body, OnChildCompleted, new[]{localVariable});
}
}

View file

@ -21,12 +21,12 @@ public class Switch : Activity
if (matchingCase != null)
{
if (matchingCase.Activity != null)
context.ScheduleActivity(matchingCase.Activity);
context.SubmitActivity(matchingCase.Activity);
return;
}
if (Default != null)
context.ScheduleActivity(Default);
context.SubmitActivity(Default);
}
private async Task<SwitchCase?> FindMatchingCaseAsync(ExpressionExecutionContext context)

View file

@ -14,7 +14,7 @@ public class While : Activity
var loop = context.Get(Condition);
if(loop)
context.ScheduleActivity(Body, OnBodyCompleted);
context.SubmitActivity(Body, OnBodyCompleted);
}
private async ValueTask OnBodyCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext)
@ -22,6 +22,6 @@ public class While : Activity
var loop = await context.EvaluateAsync(Condition);
if(loop)
context.ScheduleActivity(Body, OnBodyCompleted);
context.SubmitActivity(Body, OnBodyCompleted);
}
}

View file

@ -0,0 +1,85 @@
using Elsa.Models;
namespace Elsa.Activities.Primitives;
/// <summary>
/// Represents an inline code activity that can be used to execute arbitrary .NET code from a workflow.
/// </summary>
public class Inline : Activity
{
private readonly Func<ActivityExecutionContext, ValueTask> _activity;
public Inline(Func<ActivityExecutionContext, ValueTask> activity) => _activity = activity;
public Inline(Func<ValueTask> activity) : this(_ => activity())
{
}
public Inline(Action<ActivityExecutionContext> activity) : this(c =>
{
activity(c);
return new ValueTask();
})
{
}
public Inline(Action activity) : this(c =>
{
activity();
return new ValueTask();
})
{
}
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) => await _activity(context);
public static Inline From(Func<ActivityExecutionContext, ValueTask> activity) => new(activity);
public static Inline From(Func<ValueTask> activity) => new(activity);
public static Inline From(Action<ActivityExecutionContext> activity) => new(activity);
public static Inline From(Action activity) => new(activity);
public static Inline<T> From<T>(Func<ActivityExecutionContext, ValueTask<T>> activity) => new(activity);
public static Inline<T> From<T>(Func<ValueTask<T>> activity) => new(activity);
public static Inline<T> From<T>(Func<ActivityExecutionContext, T> activity) => new(activity);
public static Inline<T> From<T>(Func<T> activity) => new(activity);
}
/// <summary>
/// Represents an inline code activity that can be used to execute arbitrary .NET code from a workflow and return a value.
/// </summary>
public class Inline<T> : Activity<T>
{
private readonly Func<ActivityExecutionContext, ValueTask<T>> _activity;
public Inline(Func<ActivityExecutionContext, ValueTask<T>> activity, RegisterLocationReference? output = default)
{
_activity = activity;
if (output != null) Result = new Output(output);
}
public Inline(Func<ValueTask<T>> activity, RegisterLocationReference? output = default) : this(_ => activity(), output)
{
}
public Inline(Func<ActivityExecutionContext, T> activity, RegisterLocationReference? output = default) : this(c =>
{
var result = activity(c);
return new ValueTask<T>(result);
}, output)
{
}
public Inline(Func<T> activity, RegisterLocationReference? output = default) : this(c =>
{
var result = activity();
return new ValueTask<T>(result);
}, output)
{
}
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var result = await _activity(context);
context.Set(Result, result);
}
}

View file

@ -0,0 +1,62 @@
using Elsa.Activities.Primitives;
using Elsa.Attributes;
using Elsa.Contracts;
using Elsa.Models;
namespace Elsa.Activities.Workflows;
/// <summary>
/// Represents a composite activity that has a single <see cref="Root"/> activity.
/// </summary>
public class Composite : Activity
{
/// <summary>
/// The activity to schedule when this activity executes.
/// </summary>
[Outbound]
public IActivity Root { get; protected set; } = new Sequence();
protected override void Execute(ActivityExecutionContext context)
{
context.SubmitActivity(Root, OnCompletedAsync);
}
protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask;
protected static Inline From(Func<ActivityExecutionContext, ValueTask> activity) => new(activity);
protected static Inline From(Func<ValueTask> activity) => new(activity);
protected static Inline From(Action<ActivityExecutionContext> activity) => new(activity);
protected static Inline From(Action activity) => new(activity);
protected static Inline<TResult> From<TResult>(Func<ActivityExecutionContext, ValueTask<TResult>> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<ValueTask<TResult>> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<ActivityExecutionContext, TResult> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<TResult> activity, RegisterLocationReference? output = default) => new(activity, output);
}
/// <summary>
/// Represents a composite activity that has a single <see cref="Root"/> activity and returns a result.
/// </summary>
public class Composite<T> : Activity<T>
{
/// <summary>
/// The activity to schedule when this activity executes.
/// </summary>
[Outbound]
public IActivity Root { get; protected set; } = new Sequence();
protected override void Execute(ActivityExecutionContext context)
{
context.SubmitActivity(Root, OnCompletedAsync);
}
protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask;
protected static Inline From(Func<ActivityExecutionContext, ValueTask> activity) => new(activity);
protected static Inline From(Func<ValueTask> activity) => new(activity);
protected static Inline From(Action<ActivityExecutionContext> activity) => new(activity);
protected static Inline From(Action activity) => new(activity);
protected static Inline<TResult> From<TResult>(Func<ActivityExecutionContext, ValueTask<TResult>> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<ValueTask<TResult>> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<ActivityExecutionContext, TResult> activity, RegisterLocationReference? output = default) => new(activity, output);
protected static Inline<TResult> From<TResult>(Func<TResult> activity, RegisterLocationReference? output = default) => new(activity, output);
}

View file

@ -12,6 +12,13 @@ public abstract class Container : Activity, IContainer
}
protected Container(params IActivity[] activities) => Activities = activities;
protected Container(ICollection<Variable> variables, params IActivity[] activities)
{
Variables = variables;
Activities = activities;
}
[Outbound] public ICollection<IActivity> Activities { get; set; } = new List<IActivity>();
public ICollection<Variable> Variables { get; set; } = new Collection<Variable>();

View file

@ -16,7 +16,7 @@ public class Flowchart : Container
if (Start == null!)
return;
context.ScheduleActivity(Start, OnChildCompleted);
context.SubmitActivity(Start, OnChildCompleted);
}
private ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext)

View file

@ -16,6 +16,10 @@ public class Sequence : Container
public Sequence(params IActivity[] activities) : base(activities)
{
}
public Sequence(ICollection<Variable> variables, params IActivity[] activities) : base(variables, activities)
{
}
protected override void ScheduleChildren(ActivityExecutionContext context)
{
@ -31,7 +35,7 @@ public class Sequence : Container
return;
var nextActivity = childActivities.ElementAt(currentIndex);
context.ScheduleActivity(nextActivity, OnChildCompleted);
context.SubmitActivity(nextActivity, OnChildCompleted);
context.UpdateProperty<int>(CurrentIndexProperty, x => x + 1);
}

View file

@ -21,4 +21,7 @@ public static class ActivityExecutionContextExtensions
workflowExecutionContext.ExecutionLog.Add(logEntry);
return logEntry;
}
public static Variable SetVariable(this ActivityExecutionContext context, string name, object? value) => context.WorkflowExecutionContext.SetVariable(name, value);
public static T? GetVariable<T>(this ActivityExecutionContext context, string name) => context.WorkflowExecutionContext.GetVariable<T?>(name);
}

View file

@ -68,7 +68,7 @@ public class ActivityExecutionContext
/// </summary>
public IDictionary<string, object?> JournalData { get; private set; } = new Dictionary<string, object?>();
public void ScheduleActivity(IActivity? activity, ActivityCompletionCallback? completionCallback = default, IEnumerable<RegisterLocationReference>? locationReferences = default, object? tag = default)
public void SubmitActivity(IActivity? activity, ActivityCompletionCallback? completionCallback = default, IEnumerable<RegisterLocationReference>? locationReferences = default, object? tag = default)
{
if (activity == null)
return;
@ -76,7 +76,7 @@ public class ActivityExecutionContext
WorkflowExecutionContext.Schedule(activity, this, completionCallback, locationReferences, tag);
}
public void ScheduleActivity(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, IEnumerable<RegisterLocationReference>? locationReferences = default, object? tag = default)
public void SubmitActivity(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, IEnumerable<RegisterLocationReference>? locationReferences = default, object? tag = default)
{
if (activity == null)
return;
@ -89,7 +89,7 @@ public class ActivityExecutionContext
public void ScheduleActivities(IEnumerable<IActivity?> activities, ActivityCompletionCallback? completionCallback = default)
{
foreach (var activity in activities)
ScheduleActivity(activity, completionCallback);
SubmitActivity(activity, completionCallback);
}
public void CreateBookmarks(IEnumerable<object> bookmarkData, ExecuteActivityDelegate? callback = default)

View file

@ -1,17 +0,0 @@
using Elsa.Activities.Workflows;
using Elsa.Attributes;
using Elsa.Contracts;
namespace Elsa.Models;
public abstract class Composite : Activity
{
[Outbound] public IActivity Root { get; protected set; } = new Sequence();
protected override void Execute(ActivityExecutionContext context)
{
context.ScheduleActivity(Root, OnCompletedAsync);
}
protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask;
}

View file

@ -5,8 +5,17 @@ using Elsa.Models;
namespace Elsa.Modules.Scheduling.Activities;
[Activity("Scheduling", "Delays execution for the specified amount of time.")]
public class Delay : Activity
{
public Delay()
{
}
public Delay(Input<TimeSpan> timeSpan) => TimeSpan = timeSpan;
public Delay(TimeSpan timeSpan) => TimeSpan = new Input<TimeSpan>(timeSpan);
public Delay(Variable<TimeSpan> timeSpan) => TimeSpan = new Input<TimeSpan>(timeSpan);
[Input] public Input<TimeSpan> TimeSpan { get; set; } = default!;
protected override void Execute(ActivityExecutionContext context)

View file

@ -20,7 +20,7 @@ public static class VariablesWorkflow
new WriteLine(context => greeting.Get(context)),
new ReadLine
{
Output = new Output<string?>(name)
Result = new Output<string?>(name)
},
new WriteLine(new DelegateReference(context => $"Nice to meet you, {name.Get(context)}!")),
}

View file

@ -32,7 +32,7 @@ typeSystem.Register<HttpEndpoint>();
typeSystem.Register<Timer>();
functionActivityRegistry.RegisterFunction("print", nameof(WriteLine), new[] { nameof(WriteLine.Text) });
functionActivityRegistry.RegisterFunction("read", nameof(ReadLine), new[] { nameof(ReadLine.Output) });
functionActivityRegistry.RegisterFunction("read", nameof(ReadLine), new[] { nameof(ReadLine.Result) });
var assembly = Assembly.GetExecutingAssembly();
var resource = assembly.GetManifestResourceStream("Elsa.Samples.Console2.Sample1.elsa");