Skip to content

Commit 9aa190a

Browse files
authored
Merge pull request #42 from danielgerlag/refineapi
* .Schedule() API, to future date a block of steps to run in parallel to the rest of the workflow. This following example will execute the block of steps after 3 days ```c# builder .StartWith<HelloWorld>() .Schedule(data => TimeSpan.FromDays(3)).Do(block => block.StartWith<DoSomething>() .Then<DoSomethingElse>()) .Then<GoodbyeWorld>(); ``` * Overload of the .Input() method to allow access to the context object ```c# builder .StartWith<SayHello>() .ForEach(data => new List<int>() { 1, 2, 3, 4 }) .Do(x => x .StartWith<DisplayContext>() .Input(step => step.Item, (data, context) => context.Item) .Then<DoSomething>()) .Then<SayGoodbye>(); ``` ```c# builder .StartWith(context => Console.WriteLine("Hello!")) .Then(context => Console.WriteLine("Bye!")); ``` * Inline action steps API ```c# builder .StartWith(context => Console.WriteLine("Hello!")) .Then(context => Console.WriteLine("Bye!")); ``` * Discontinued support for .NET 4.5.2 (.NET 4.6 is .NET Standard 1.3 compatible)
2 parents cfe8dfb + f4a3bd9 commit 9aa190a

File tree

26 files changed

+330
-91
lines changed

26 files changed

+330
-91
lines changed

ReleaseNotes/1.2.8.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Workflow Core 1.2.8
2+
3+
* .Schedule() API, to future date a block of steps to run in parallel to the rest of the workflow.
4+
5+
This following example will execute the block of steps after 3 days
6+
```c#
7+
builder
8+
.StartWith<HelloWorld>()
9+
.Schedule(data => TimeSpan.FromDays(3)).Do(block =>
10+
block.StartWith<DoSomething>()
11+
.Then<DoSomethingElse>())
12+
.Then<GoodbyeWorld>();
13+
```
14+
15+
* Overload of the .Input() method to allow access to the context object
16+
17+
```c#
18+
builder
19+
.StartWith<SayHello>()
20+
.ForEach(data => new List<int>() { 1, 2, 3, 4 })
21+
.Do(x => x
22+
.StartWith<DisplayContext>()
23+
.Input(step => step.Item, (data, context) => context.Item)
24+
.Then<DoSomething>())
25+
.Then<SayGoodbye>();
26+
```
27+
28+
```c#
29+
builder
30+
.StartWith(context => Console.WriteLine("Hello!"))
31+
.Then(context => Console.WriteLine("Bye!"));
32+
```
33+
34+
35+
* Inline action steps API
36+
37+
```c#
38+
builder
39+
.StartWith(context => Console.WriteLine("Hello!"))
40+
.Then(context => Console.WriteLine("Bye!"));
41+
```
42+
43+
* Discontinued support for .NET 4.5.2 (.NET 4.6 is .NET Standard 1.3 compatible)

WorkflowCore.sln

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26228.4
4+
VisualStudioVersion = 15.0.26430.12
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}"
77
EndProject
@@ -88,6 +88,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample13", "sr
8888
EndProject
8989
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.Azure", "src\providers\WorkflowCore.Providers.Azure\WorkflowCore.Providers.Azure.csproj", "{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}"
9090
EndProject
91+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNotes", "{38ECB00C-3F3B-4442-8408-ACE3B37FFAA8}"
92+
ProjectSection(SolutionItems) = preProject
93+
ReleaseNotes\1.2.8.md = ReleaseNotes\1.2.8.md
94+
EndProjectSection
95+
EndProject
9196
Global
9297
GlobalSection(SolutionConfigurationPlatforms) = preSolution
9398
Debug|Any CPU = Debug|Any CPU

src/WorkflowCore/Interface/IStepBuilder.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public interface IStepBuilder<TData, TStepBody>
4545
/// <returns></returns>
4646
IStepBuilder<TData, InlineStepBody> Then(Func<IStepExecutionContext, ExecutionResult> body);
4747

48+
/// <summary>
49+
/// Specify an inline next step in the workflow
50+
/// </summary>
51+
/// <param name="body"></param>
52+
/// <returns></returns>
53+
IStepBuilder<TData, ActionStepBody> Then(Action<IStepExecutionContext> body);
54+
4855
/// <summary>
4956
/// Configure an outcome for this step, then wire it to another step
5057
/// </summary>
@@ -62,6 +69,15 @@ public interface IStepBuilder<TData, TStepBody>
6269
/// <returns></returns>
6370
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, TInput>> value);
6471

72+
/// <summary>
73+
/// Map properties on the step to properties on the workflow data object before the step executes
74+
/// </summary>
75+
/// <typeparam name="TInput"></typeparam>
76+
/// <param name="stepProperty"></param>
77+
/// <param name="value"></param>
78+
/// <returns></returns>
79+
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value);
80+
6581
/// <summary>
6682
/// Map properties on the workflow data object to properties on the step after the step executes
6783
/// </summary>
@@ -76,11 +92,18 @@ public interface IStepBuilder<TData, TStepBody>
7692
/// </summary>
7793
/// <param name="eventName"></param>
7894
/// <param name="eventKey"></param>
95+
/// <param name="effectiveDate"></param>
7996
/// <returns></returns>
8097
IStepBuilder<TData, SubscriptionStepBody> WaitFor(string eventName, Expression<Func<TData, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null);
8198

8299
IStepBuilder<TData, TStep> End<TStep>(string name) where TStep : IStepBody;
83100

101+
/// <summary>
102+
/// Configure the behavior when this step throws an unhandled exception
103+
/// </summary>
104+
/// <param name="behavior"></param>
105+
/// <param name="retryInterval"></param>
106+
/// <returns></returns>
84107
IStepBuilder<TData, TStepBody> OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
85108

86109
/// <summary>
@@ -89,10 +112,32 @@ public interface IStepBuilder<TData, TStepBody>
89112
/// <returns></returns>
90113
IStepBuilder<TData, TStepBody> EndWorkflow();
91114

115+
/// <summary>
116+
/// Wait for a specified period
117+
/// </summary>
118+
/// <param name="period"></param>
119+
/// <returns></returns>
120+
IStepBuilder<TData, Delay> Delay(Expression<Func<TData, TimeSpan>> period);
121+
122+
/// <summary>
123+
/// Execute a block of steps, once for each item in a collection in a parallel foreach
124+
/// </summary>
125+
/// <param name="collection"></param>
126+
/// <returns></returns>
92127
IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection);
93128

129+
/// <summary>
130+
/// Repeat a block of steps until a condition becomes true
131+
/// </summary>
132+
/// <param name="condition"></param>
133+
/// <returns></returns>
94134
IContainerStepBuilder<TData, While, While> While(Expression<Func<TData, bool>> condition);
95135

136+
/// <summary>
137+
/// Execute a block of steps if a condition is true
138+
/// </summary>
139+
/// <param name="condition"></param>
140+
/// <returns></returns>
96141
IContainerStepBuilder<TData, If, If> If(Expression<Func<TData, bool>> condition);
97142

98143
/// <summary>
@@ -102,6 +147,17 @@ public interface IStepBuilder<TData, TStepBody>
102147
/// <returns></returns>
103148
IContainerStepBuilder<TData, When, OutcomeSwitch> When(Expression<Func<TData, object>> outcomeValue, string label = null);
104149

150+
/// <summary>
151+
/// Execute multiple blocks of steps in parallel
152+
/// </summary>
153+
/// <returns></returns>
105154
IParallelStepBuilder<TData, Sequence> Parallel();
155+
156+
/// <summary>
157+
/// Schedule a block of steps to execute in parallel sometime in the future
158+
/// </summary>
159+
/// <param name="time"></param>
160+
/// <returns></returns>
161+
IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time);
106162
}
107163
}

src/WorkflowCore/Interface/IWorkflowBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public interface IWorkflowBuilder<TData> : IWorkflowBuilder
2424

2525
IStepBuilder<TData, InlineStepBody> StartWith(Func<IStepExecutionContext, ExecutionResult> body);
2626

27+
IStepBuilder<TData, ActionStepBody> StartWith(Action<IStepExecutionContext> body);
28+
2729
IEnumerable<WorkflowStep> GetUpstreamSteps(int id);
2830

2931
IWorkflowBuilder<TData> UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace WorkflowCore.Models
6+
{
7+
public class SchedulePersistenceData
8+
{
9+
public bool Elapsed { get; set; }
10+
11+
}
12+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Linq.Expressions;
5+
using System.Threading.Tasks;
6+
using WorkflowCore.Interface;
7+
using WorkflowCore.Models;
8+
9+
namespace WorkflowCore.Primitives
10+
{
11+
public class ActionStepBody : StepBody
12+
{
13+
public Action<IStepExecutionContext> Body { get; set; }
14+
15+
public override ExecutionResult Run(IStepExecutionContext context)
16+
{
17+
Body(context);
18+
return ExecutionResult.Next();
19+
}
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using WorkflowCore.Interface;
6+
using WorkflowCore.Models;
7+
8+
namespace WorkflowCore.Primitives
9+
{
10+
public class Delay : StepBody
11+
{
12+
public TimeSpan Period { get; set; }
13+
14+
public override ExecutionResult Run(IStepExecutionContext context)
15+
{
16+
if (context.PersistenceData != null)
17+
return ExecutionResult.Next();
18+
19+
return ExecutionResult.Sleep(Period, true);
20+
}
21+
}
22+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using WorkflowCore.Interface;
6+
using WorkflowCore.Models;
7+
8+
namespace WorkflowCore.Primitives
9+
{
10+
public class Schedule : ContainerStepBody
11+
{
12+
public TimeSpan Period { get; set; }
13+
14+
public override ExecutionResult Run(IStepExecutionContext context)
15+
{
16+
if (context.PersistenceData == null)
17+
return ExecutionResult.Sleep(Period, new SchedulePersistenceData() { Elapsed = false });
18+
19+
20+
if (context.PersistenceData is SchedulePersistenceData)
21+
{
22+
if (!((SchedulePersistenceData) context.PersistenceData).Elapsed)
23+
return ExecutionResult.Branch(new List<object>() { null }, new SchedulePersistenceData() { Elapsed = true });
24+
25+
var complete = true;
26+
27+
foreach (var childId in context.ExecutionPointer.Children)
28+
complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId);
29+
30+
if (complete)
31+
return ExecutionResult.Next();
32+
33+
return ExecutionResult.Persist(context.PersistenceData);
34+
}
35+
36+
throw new ArgumentException();
37+
}
38+
}
39+
}

src/WorkflowCore/Services/StepBuilder.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ public IStepBuilder<TData, InlineStepBody> Then(Func<IStepExecutionContext, Exec
6363
return stepBuilder;
6464
}
6565

66+
public IStepBuilder<TData, ActionStepBody> Then(Action<IStepExecutionContext> body)
67+
{
68+
var newStep = new WorkflowStep<ActionStepBody>();
69+
WorkflowBuilder.AddStep(newStep);
70+
var stepBuilder = new StepBuilder<TData, ActionStepBody>(WorkflowBuilder, newStep);
71+
stepBuilder.Input(x => x.Body, x => body);
72+
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
73+
return stepBuilder;
74+
}
75+
6676
public IStepOutcomeBuilder<TData> When(object outcomeValue, string label = null)
6777
{
6878
StepOutcome result = new StepOutcome();
@@ -82,6 +92,15 @@ public IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, T
8292
return this;
8393
}
8494

95+
public IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value)
96+
{
97+
var mapping = new DataMapping();
98+
mapping.Source = value;
99+
mapping.Target = stepProperty;
100+
Step.Inputs.Add(mapping);
101+
return this;
102+
}
103+
85104
public IStepBuilder<TData, TStepBody> Output<TOutput>(Expression<Func<TData, TOutput>> dataProperty, Expression<Func<TStepBody, TOutput>> value)
86105
{
87106
var mapping = new DataMapping();
@@ -150,6 +169,27 @@ public IStepBuilder<TData, TStepBody> EndWorkflow()
150169
return this;
151170
}
152171

172+
public IStepBuilder<TData, Delay> Delay(Expression<Func<TData, TimeSpan>> period)
173+
{
174+
var newStep = new WorkflowStep<Delay>();
175+
176+
Expression<Func<Delay, TimeSpan>> inputExpr = (x => x.Period);
177+
178+
var mapping = new DataMapping()
179+
{
180+
Source = period,
181+
Target = inputExpr
182+
};
183+
newStep.Inputs.Add(mapping);
184+
185+
WorkflowBuilder.AddStep(newStep);
186+
var stepBuilder = new StepBuilder<TData, Delay>(WorkflowBuilder, newStep);
187+
188+
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
189+
190+
return stepBuilder;
191+
}
192+
153193
public IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection)
154194
{
155195
var newStep = new WorkflowStep<Foreach>();
@@ -244,7 +284,7 @@ public IContainerStepBuilder<TData, When, OutcomeSwitch> When(Expression<Func<TD
244284

245285
WorkflowBuilder.AddStep(newStep);
246286
var stepBuilder = new SkipStepBuilder<TData, When, OutcomeSwitch>(WorkflowBuilder, newStep, switchBuilder);
247-
287+
248288
switchBuilder.Step.Children.Add(newStep.Id);
249289

250290
return stepBuilder;
@@ -262,6 +302,27 @@ public IParallelStepBuilder<TData, Sequence> Parallel()
262302
return stepBuilder;
263303
}
264304

305+
public IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time)
306+
{
307+
var newStep = new WorkflowStep<Schedule>();
308+
309+
Expression<Func<Schedule, TimeSpan>> inputExpr = (x => x.Period);
310+
311+
var mapping = new DataMapping()
312+
{
313+
Source = time,
314+
Target = inputExpr
315+
};
316+
newStep.Inputs.Add(mapping);
317+
318+
WorkflowBuilder.AddStep(newStep);
319+
var stepBuilder = new SkipStepBuilder<TData, Schedule, TStepBody>(WorkflowBuilder, newStep, this);
320+
321+
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
322+
323+
return stepBuilder;
324+
}
325+
265326
public IStepBuilder<TData, TStepBody> Do(Action<IWorkflowBuilder<TData>> builder)
266327
{
267328
builder.Invoke(WorkflowBuilder);

src/WorkflowCore/Services/WorkflowBuilder.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ public IStepBuilder<TData, InlineStepBody> StartWith(Func<IStepExecutionContext,
8282
return stepBuilder;
8383
}
8484

85+
public IStepBuilder<TData, ActionStepBody> StartWith(Action<IStepExecutionContext> body)
86+
{
87+
var newStep = new WorkflowStep<ActionStepBody>();
88+
AddStep(newStep);
89+
var stepBuilder = new StepBuilder<TData, ActionStepBody>(this, newStep);
90+
stepBuilder.Input(x => x.Body, x => body);
91+
return stepBuilder;
92+
}
93+
8594
public IEnumerable<WorkflowStep> GetUpstreamSteps(int id)
8695
{
8796
return Steps.Where(x => x.Outcomes.Any(y => y.NextStep == id)).ToList();

0 commit comments

Comments
 (0)