Skip to content

Commit 2d1962c

Browse files
committed
ActivityName as expression. It solves #542 issue.
1 parent 018b9e9 commit 2d1962c

File tree

12 files changed

+302
-0
lines changed

12 files changed

+302
-0
lines changed

WorkflowCore.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "sr
152152
EndProject
153153
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}"
154154
EndProject
155+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample20", "src\samples\WorkflowCore.Sample20\WorkflowCore.Sample20.csproj", "{68D1B955-1049-477B-A894-13FCB96C45DE}"
156+
EndProject
155157
Global
156158
GlobalSection(SolutionConfigurationPlatforms) = preSolution
157159
Debug|Any CPU = Debug|Any CPU
@@ -374,6 +376,10 @@ Global
374376
{AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Debug|Any CPU.Build.0 = Debug|Any CPU
375377
{AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.ActiveCfg = Release|Any CPU
376378
{AF205715-C8B7-42EF-BF14-AFC9E7F27242}.Release|Any CPU.Build.0 = Release|Any CPU
379+
{68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
380+
{68D1B955-1049-477B-A894-13FCB96C45DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
381+
{68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
382+
{68D1B955-1049-477B-A894-13FCB96C45DE}.Release|Any CPU.Build.0 = Release|Any CPU
377383
EndGlobalSection
378384
GlobalSection(SolutionProperties) = preSolution
379385
HideSolutionNode = FALSE
@@ -436,6 +442,7 @@ Global
436442
{54DE20BA-EBA7-4BF0-9BD9-F03766849716} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB}
437443
{1223ED47-3E5E-4960-B70D-DFAF550F6666} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
438444
{AF205715-C8B7-42EF-BF14-AFC9E7F27242} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2}
445+
{68D1B955-1049-477B-A894-13FCB96C45DE} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
439446
EndGlobalSection
440447
GlobalSection(ExtensibilityGlobals) = postSolution
441448
SolutionGuid = {DC0FA8D3-6449-4FDA-BB46-ECF58FAD23B4}

src/WorkflowCore/Interface/IWorkflowModifier.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,15 @@ IContainerStepBuilder<TData, Recur, TStepBody> Recur(Expression<Func<TData, Time
152152
IStepBuilder<TData, Activity> Activity(string activityName, Expression<Func<TData, object>> parameters = null,
153153
Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null);
154154

155+
/// <summary>
156+
/// Wait here until an external activity is complete
157+
/// </summary>
158+
/// <param name="activityName">The name used to identify the activity to wait for</param>
159+
/// <param name="parameters">The data to pass the external activity worker</param>
160+
/// <param name="effectiveDate">Listen for events as of this effective date</param>
161+
/// <param name="cancelCondition">A conditon that when true will cancel this WaitFor</param>
162+
/// <returns></returns>
163+
IStepBuilder<TData, Activity> Activity(Expression<Func<TData, IStepExecutionContext, string>> activityName, Expression<Func<TData, object>> parameters = null,
164+
Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null);
155165
}
156166
}

src/WorkflowCore/Services/FluentBuilders/StepBuilder.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,5 +522,24 @@ public IStepBuilder<TData, Activity> Activity(string activityName, Expression<Fu
522522
Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id });
523523
return stepBuilder;
524524
}
525+
526+
public IStepBuilder<TData, Activity> Activity(Expression<Func<TData, IStepExecutionContext, string>> activityName, Expression<Func<TData, object>> parameters = null, Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null)
527+
{
528+
var newStep = new WorkflowStep<Activity>();
529+
newStep.CancelCondition = cancelCondition;
530+
531+
WorkflowBuilder.AddStep(newStep);
532+
var stepBuilder = new StepBuilder<TData, Activity>(WorkflowBuilder, newStep);
533+
stepBuilder.Input((step) => step.ActivityName, activityName);
534+
535+
if (parameters != null)
536+
stepBuilder.Input((step) => step.Parameters, parameters);
537+
538+
if (effectiveDate != null)
539+
stepBuilder.Input((step) => step.EffectiveDate, effectiveDate);
540+
541+
Step.Outcomes.Add(new ValueOutcome { NextStep = newStep.Id });
542+
return stepBuilder;
543+
}
525544
}
526545
}

src/WorkflowCore/Services/FluentBuilders/WorkflowBuilder.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ public IStepBuilder<TData, Activity> Activity(string activityName, Expression<Fu
271271
{
272272
return Start().Activity(activityName, parameters, effectiveDate, cancelCondition);
273273
}
274+
public IStepBuilder<TData, Activity> Activity(Expression<Func<TData, IStepExecutionContext, string>> activityName, Expression<Func<TData, object>> parameters = null, Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null)
275+
{
276+
return Start().Activity(activityName, parameters, effectiveDate, cancelCondition);
277+
}
274278

275279
private IStepBuilder<TData, InlineStepBody> Start()
276280
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Sample20.Steps;
4+
5+
namespace WorkflowCore.Sample20
6+
{
7+
class ActivityWorkflow : IWorkflow<MyData>
8+
{
9+
public string Id => "activity-sample";
10+
public int Version => 1;
11+
12+
public void Build(IWorkflowBuilder<MyData> builder)
13+
{
14+
builder
15+
.StartWith<HelloWorld>()
16+
.Activity((data, context) => "get-approval-" + context.Workflow.Id, (data) => data.Request)
17+
.Output(data => data.ApprovedBy, step => step.Result)
18+
.Then<CustomMessage>()
19+
.Input(step => step.Message, data => "Approved by " + data.ApprovedBy)
20+
.Then<GoodbyeWorld>();
21+
}
22+
}
23+
24+
class MyData
25+
{
26+
public string Request { get; set; }
27+
public string ApprovedBy { get; set; }
28+
}
29+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Logging;
3+
using System;
4+
using WorkflowCore.Interface;
5+
6+
namespace WorkflowCore.Sample20
7+
{
8+
class Program
9+
{
10+
static void Main(string[] args)
11+
{
12+
var serviceProvider = ConfigureServices();
13+
14+
//start the workflow host
15+
var host = serviceProvider.GetService<IWorkflowHost>();
16+
host.RegisterWorkflow<ActivityWorkflow, MyData>();
17+
host.Start();
18+
19+
Console.WriteLine("Starting workflow...");
20+
21+
var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result;
22+
23+
var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result;
24+
25+
if (approval != null)
26+
{
27+
Console.WriteLine("Approval required for " + approval.Parameters);
28+
host.SubmitActivitySuccess(approval.Token, "John Smith");
29+
}
30+
31+
Console.ReadLine();
32+
host.Stop();
33+
}
34+
35+
private static IServiceProvider ConfigureServices()
36+
{
37+
//setup dependency injection
38+
IServiceCollection services = new ServiceCollection();
39+
//services.AddWorkflow();
40+
services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow"));
41+
//services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true));
42+
//services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true));
43+
services.AddLogging(cfg =>
44+
{
45+
cfg.AddConsole();
46+
cfg.AddDebug();
47+
});
48+
49+
var serviceProvider = services.BuildServiceProvider();
50+
return serviceProvider;
51+
}
52+
}
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Activity sample
2+
3+
Illustrates how to have your workflow wait for an external activity that is fulfilled by a worker that you implement.
4+
5+
This workflow will wait for the `get-approval-{workflowId}` activity and pass the request string to it as an input.
6+
7+
The main reason behind of this example is Activities are global listeners. Therefore, in some cases, you want to be sure about its uniqueness.
8+
9+
Also, you can take look the issue https://github.com/danielgerlag/workflow-core/issues/542
10+
11+
```c#
12+
builder
13+
.StartWith<HelloWorld>()
14+
.Activity((data, context) => context.Workflow.Id, (data) => data.Request)
15+
.Output(data => data.ApprovedBy, step => step.Result)
16+
.Then<CustomMessage>()
17+
.Input(step => step.Message, data => "Approved by " + data.ApprovedBy)
18+
.Then<GoodbyeWorld>();
19+
```
20+
21+
Then we implement an activity worker to pull pending activities of type `get-approval`, where we can inspect the input and submit a response back to the waiting workflow.
22+
23+
```c#
24+
var workflowId = host.StartWorkflow("activity-sample", new MyData { Request = "Spend $1,000,000" }).Result;
25+
26+
var approval = host.GetPendingActivity("get-approval-" + workflowId, "worker1", TimeSpan.FromMinutes(1)).Result;
27+
28+
if (approval != null)
29+
{
30+
Console.WriteLine("Approval required for " + approval.Parameters);
31+
host.SubmitActivitySuccess(approval.Token, "John Smith");
32+
}
33+
```
34+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Linq;
3+
using WorkflowCore.Interface;
4+
using WorkflowCore.Models;
5+
6+
namespace WorkflowCore.Sample20.Steps
7+
{
8+
public class CustomMessage : StepBody
9+
{
10+
11+
public string Message { get; set; }
12+
13+
public override ExecutionResult Run(IStepExecutionContext context)
14+
{
15+
Console.WriteLine(Message);
16+
return ExecutionResult.Next();
17+
}
18+
}
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Linq;
3+
using WorkflowCore.Interface;
4+
using WorkflowCore.Models;
5+
6+
namespace WorkflowCore.Sample20.Steps
7+
{
8+
public class GoodbyeWorld : StepBody
9+
{
10+
public override ExecutionResult Run(IStepExecutionContext context)
11+
{
12+
Console.WriteLine("Goodbye world");
13+
return ExecutionResult.Next();
14+
}
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Linq;
3+
using WorkflowCore.Interface;
4+
using WorkflowCore.Models;
5+
6+
namespace WorkflowCore.Sample20.Steps
7+
{
8+
public class HelloWorld : StepBody
9+
{
10+
public override ExecutionResult Run(IStepExecutionContext context)
11+
{
12+
Console.WriteLine("Hello world");
13+
return ExecutionResult.Next();
14+
}
15+
}
16+
}

0 commit comments

Comments
 (0)