Skip to content

Commit

Permalink
Merge pull request #42 from danielgerlag/refineapi
Browse files Browse the repository at this point in the history
* .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)
  • Loading branch information
danielgerlag authored Jun 17, 2017
2 parents cfe8dfb + f4a3bd9 commit 9aa190a
Show file tree
Hide file tree
Showing 26 changed files with 330 additions and 91 deletions.
43 changes: 43 additions & 0 deletions ReleaseNotes/1.2.8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Workflow Core 1.2.8

* .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)
7 changes: 6 additions & 1 deletion WorkflowCore.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26430.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}"
EndProject
Expand Down Expand Up @@ -88,6 +88,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample13", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.Azure", "src\providers\WorkflowCore.Providers.Azure\WorkflowCore.Providers.Azure.csproj", "{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNotes", "{38ECB00C-3F3B-4442-8408-ACE3B37FFAA8}"
ProjectSection(SolutionItems) = preProject
ReleaseNotes\1.2.8.md = ReleaseNotes\1.2.8.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
56 changes: 56 additions & 0 deletions src/WorkflowCore/Interface/IStepBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ public interface IStepBuilder<TData, TStepBody>
/// <returns></returns>
IStepBuilder<TData, InlineStepBody> Then(Func<IStepExecutionContext, ExecutionResult> body);

/// <summary>
/// Specify an inline next step in the workflow
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
IStepBuilder<TData, ActionStepBody> Then(Action<IStepExecutionContext> body);

/// <summary>
/// Configure an outcome for this step, then wire it to another step
/// </summary>
Expand All @@ -62,6 +69,15 @@ public interface IStepBuilder<TData, TStepBody>
/// <returns></returns>
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, TInput>> value);

/// <summary>
/// Map properties on the step to properties on the workflow data object before the step executes
/// </summary>
/// <typeparam name="TInput"></typeparam>
/// <param name="stepProperty"></param>
/// <param name="value"></param>
/// <returns></returns>
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value);

/// <summary>
/// Map properties on the workflow data object to properties on the step after the step executes
/// </summary>
Expand All @@ -76,11 +92,18 @@ public interface IStepBuilder<TData, TStepBody>
/// </summary>
/// <param name="eventName"></param>
/// <param name="eventKey"></param>
/// <param name="effectiveDate"></param>
/// <returns></returns>
IStepBuilder<TData, SubscriptionStepBody> WaitFor(string eventName, Expression<Func<TData, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null);

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

/// <summary>
/// Configure the behavior when this step throws an unhandled exception
/// </summary>
/// <param name="behavior"></param>
/// <param name="retryInterval"></param>
/// <returns></returns>
IStepBuilder<TData, TStepBody> OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);

/// <summary>
Expand All @@ -89,10 +112,32 @@ public interface IStepBuilder<TData, TStepBody>
/// <returns></returns>
IStepBuilder<TData, TStepBody> EndWorkflow();

/// <summary>
/// Wait for a specified period
/// </summary>
/// <param name="period"></param>
/// <returns></returns>
IStepBuilder<TData, Delay> Delay(Expression<Func<TData, TimeSpan>> period);

/// <summary>
/// Execute a block of steps, once for each item in a collection in a parallel foreach
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection);

/// <summary>
/// Repeat a block of steps until a condition becomes true
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
IContainerStepBuilder<TData, While, While> While(Expression<Func<TData, bool>> condition);

/// <summary>
/// Execute a block of steps if a condition is true
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
IContainerStepBuilder<TData, If, If> If(Expression<Func<TData, bool>> condition);

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

/// <summary>
/// Execute multiple blocks of steps in parallel
/// </summary>
/// <returns></returns>
IParallelStepBuilder<TData, Sequence> Parallel();

/// <summary>
/// Schedule a block of steps to execute in parallel sometime in the future
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time);
}
}
2 changes: 2 additions & 0 deletions src/WorkflowCore/Interface/IWorkflowBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public interface IWorkflowBuilder<TData> : IWorkflowBuilder

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

IStepBuilder<TData, ActionStepBody> StartWith(Action<IStepExecutionContext> body);

IEnumerable<WorkflowStep> GetUpstreamSteps(int id);

IWorkflowBuilder<TData> UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
Expand Down
12 changes: 12 additions & 0 deletions src/WorkflowCore/Models/SchedulePersistenceData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace WorkflowCore.Models
{
public class SchedulePersistenceData
{
public bool Elapsed { get; set; }

}
}
21 changes: 21 additions & 0 deletions src/WorkflowCore/Primitives/ActionStepBody.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using WorkflowCore.Interface;
using WorkflowCore.Models;

namespace WorkflowCore.Primitives
{
public class ActionStepBody : StepBody
{
public Action<IStepExecutionContext> Body { get; set; }

public override ExecutionResult Run(IStepExecutionContext context)
{
Body(context);
return ExecutionResult.Next();
}
}
}
22 changes: 22 additions & 0 deletions src/WorkflowCore/Primitives/Delay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WorkflowCore.Interface;
using WorkflowCore.Models;

namespace WorkflowCore.Primitives
{
public class Delay : StepBody
{
public TimeSpan Period { get; set; }

public override ExecutionResult Run(IStepExecutionContext context)
{
if (context.PersistenceData != null)
return ExecutionResult.Next();

return ExecutionResult.Sleep(Period, true);
}
}
}
39 changes: 39 additions & 0 deletions src/WorkflowCore/Primitives/Schedule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WorkflowCore.Interface;
using WorkflowCore.Models;

namespace WorkflowCore.Primitives
{
public class Schedule : ContainerStepBody
{
public TimeSpan Period { get; set; }

public override ExecutionResult Run(IStepExecutionContext context)
{
if (context.PersistenceData == null)
return ExecutionResult.Sleep(Period, new SchedulePersistenceData() { Elapsed = false });


if (context.PersistenceData is SchedulePersistenceData)
{
if (!((SchedulePersistenceData) context.PersistenceData).Elapsed)
return ExecutionResult.Branch(new List<object>() { null }, new SchedulePersistenceData() { Elapsed = true });

var complete = true;

foreach (var childId in context.ExecutionPointer.Children)
complete = complete && IsBranchComplete(context.Workflow.ExecutionPointers, childId);

if (complete)
return ExecutionResult.Next();

return ExecutionResult.Persist(context.PersistenceData);
}

throw new ArgumentException();
}
}
}
63 changes: 62 additions & 1 deletion src/WorkflowCore/Services/StepBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ public IStepBuilder<TData, InlineStepBody> Then(Func<IStepExecutionContext, Exec
return stepBuilder;
}

public IStepBuilder<TData, ActionStepBody> Then(Action<IStepExecutionContext> body)
{
var newStep = new WorkflowStep<ActionStepBody>();
WorkflowBuilder.AddStep(newStep);
var stepBuilder = new StepBuilder<TData, ActionStepBody>(WorkflowBuilder, newStep);
stepBuilder.Input(x => x.Body, x => body);
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
return stepBuilder;
}

public IStepOutcomeBuilder<TData> When(object outcomeValue, string label = null)
{
StepOutcome result = new StepOutcome();
Expand All @@ -82,6 +92,15 @@ public IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, T
return this;
}

public IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value)
{
var mapping = new DataMapping();
mapping.Source = value;
mapping.Target = stepProperty;
Step.Inputs.Add(mapping);
return this;
}

public IStepBuilder<TData, TStepBody> Output<TOutput>(Expression<Func<TData, TOutput>> dataProperty, Expression<Func<TStepBody, TOutput>> value)
{
var mapping = new DataMapping();
Expand Down Expand Up @@ -150,6 +169,27 @@ public IStepBuilder<TData, TStepBody> EndWorkflow()
return this;
}

public IStepBuilder<TData, Delay> Delay(Expression<Func<TData, TimeSpan>> period)
{
var newStep = new WorkflowStep<Delay>();

Expression<Func<Delay, TimeSpan>> inputExpr = (x => x.Period);

var mapping = new DataMapping()
{
Source = period,
Target = inputExpr
};
newStep.Inputs.Add(mapping);

WorkflowBuilder.AddStep(newStep);
var stepBuilder = new StepBuilder<TData, Delay>(WorkflowBuilder, newStep);

Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });

return stepBuilder;
}

public IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection)
{
var newStep = new WorkflowStep<Foreach>();
Expand Down Expand Up @@ -244,7 +284,7 @@ public IContainerStepBuilder<TData, When, OutcomeSwitch> When(Expression<Func<TD

WorkflowBuilder.AddStep(newStep);
var stepBuilder = new SkipStepBuilder<TData, When, OutcomeSwitch>(WorkflowBuilder, newStep, switchBuilder);

switchBuilder.Step.Children.Add(newStep.Id);

return stepBuilder;
Expand All @@ -262,6 +302,27 @@ public IParallelStepBuilder<TData, Sequence> Parallel()
return stepBuilder;
}

public IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time)
{
var newStep = new WorkflowStep<Schedule>();

Expression<Func<Schedule, TimeSpan>> inputExpr = (x => x.Period);

var mapping = new DataMapping()
{
Source = time,
Target = inputExpr
};
newStep.Inputs.Add(mapping);

WorkflowBuilder.AddStep(newStep);
var stepBuilder = new SkipStepBuilder<TData, Schedule, TStepBody>(WorkflowBuilder, newStep, this);

Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });

return stepBuilder;
}

public IStepBuilder<TData, TStepBody> Do(Action<IWorkflowBuilder<TData>> builder)
{
builder.Invoke(WorkflowBuilder);
Expand Down
9 changes: 9 additions & 0 deletions src/WorkflowCore/Services/WorkflowBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ public IStepBuilder<TData, InlineStepBody> StartWith(Func<IStepExecutionContext,
return stepBuilder;
}

public IStepBuilder<TData, ActionStepBody> StartWith(Action<IStepExecutionContext> body)
{
var newStep = new WorkflowStep<ActionStepBody>();
AddStep(newStep);
var stepBuilder = new StepBuilder<TData, ActionStepBody>(this, newStep);
stepBuilder.Input(x => x.Body, x => body);
return stepBuilder;
}

public IEnumerable<WorkflowStep> GetUpstreamSteps(int id)
{
return Steps.Where(x => x.Outcomes.Any(y => y.NextStep == id)).ToList();
Expand Down
Loading

0 comments on commit 9aa190a

Please sign in to comment.