步骤(step)
工作流由一系列相连的步骤组成,每个步骤都可以有输入和输出,这些输出也可以传递到它所在的工作流。可以通过StepBody
和StepBodyAsync
抽象类来创建步骤并实现Run/RunAsync
方法,该方法也可以在定义工作流的时候内联创建。
1 2 3 4 5 6 7 8
| public class HelloWorld : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Hello world"); return ExecutionResult.Next(); } }
|
StepBody
和StepBodyAsync
是由WrokflowCore宿主创建,在创建的时候首先尝试使用IServiceProvider进行依赖注入,如果无法使用则搜索无参构造函数。
工作流
通过组成一系列步骤来组成工作流,通过实现IWorkflow接口来实现
1 2 3 4 5 6 7 8 9 10 11 12
| public class HelloWorldWorkflow : IWorkflow { public string Id => "HelloWorld"; public int Version => 1;
public void Build(IWorkflowBuilder<object> builder) { builder .StartWith<HelloWorld>() .Then<GoodbyeWorld>(); } }
|
使用内联方式定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class HelloWorldWorkflow : IWorkflow { public string Id => "HelloWorld"; public int Version => 1;
public void Build(IWorkflowBuilder<object> builder) { builder .StartWith(context => { Console.WriteLine("Hello world"); return ExecutionResult.Next(); }) .Then(context => { Console.WriteLine("Goodbye world"); return ExecutionResult.Next(); }); } }
|
工作流中的每个步骤都可以保存在数据库中,可以将工作流中的某个步骤在以后的执行。
配置
可以使用IServiceCollection
的AddWorkflow
扩展方法将服务进行注册。默认配置了MemoryPersistenceProvider
和SingleNodeConcurrencyProvider
用于测试。同时开发者也可以配置数据库,用来实现工作流的持久化。
services.AddWorkflow();
工作流的使用
从容器中获取工作流服务,一定要调用RegisterWorkflow进行注册,然后调用start来启动工作流线程池,并使用StartWorkflow启动特定工作流。
1 2 3 4 5 6 7 8
| var host = serviceProvider.GetService<IWorkflowHost>(); host.RegisterWorkflow<HelloWorldWorkflow>(); host.Start();
host.StartWorkflow("HelloWorld", 1, null);
Console.ReadLine(); host.Stop();
|
步骤之间传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class AddNumbers : StepBody { public int Input1 { get; set; }
public int Input2 { get; set; }
public int Output { get; set; }
public override ExecutionResult Run(IStepExecutionContext context) { Output = (Input1 + Input2); return ExecutionResult.Next(); } }
public class MyDataClass { public int Value1 { get; set; } public int Value2 { get; set; } public int Answer { get; set; } }
public class PassingDataWorkflow : IWorkflow<MyDataClass> { public void Build(IWorkflowBuilder<MyDataClass> builder) { builder .StartWith<AddNumbers>() .Input(step => step.Input1, data => data.Value1) .Input(step => step.Input2, data => data.Value2) .Output(data => data.Answer, step => step.Output) .Then<CustomMessage>() .Input(step => step.Message, data => "The answer is " + data.Answer.ToString()); } ... }
|
步骤的依赖注入
如果使用IOC容器注入步骤,工作流则会使用容器来注入步骤的依赖项
需要注入依赖的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public interface IMyService { void DoTheThings(); } ... public class MyService : IMyService { public void DoTheThings() { Console.WriteLine("Doing stuff..."); } }
IServiceCollection services = new ServiceCollection(); services.AddWorkflow();
services.AddTransient<DoSomething>(); services.AddTransient<IMyService, MyService>();
public class DoSomething : StepBody { private IMyService _myService;
public DoSomething(IMyService myService) { _myService = myService; }
public override ExecutionResult Run(IStepExecutionContext context) { _myService.DoTheThings(); return ExecutionResult.Next(); } }
|
案例1 基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class GoodbyeWorld : StepBody {
private ILogger _logger;
public GoodbyeWorld(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<GoodbyeWorld>(); }
public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Goodbye world"); _logger.LogInformation("Hi there!"); return ExecutionResult.Next(); } }
public class HelloWorld : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Hello world"); return ExecutionResult.Next(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class HelloWorldWorkflow : IWorkflow { public void Build(IWorkflowBuilder<object> builder) { builder .UseDefaultErrorBehavior(WorkflowErrorHandling.Suspend) .StartWith<HelloWorld>() .Then<GoodbyeWorld>(); }
public string Id => "HelloWorld"; public int Version => 1; }
|
1 2 3 4 5 6 7 8 9 10 11
| IServiceCollection services = new ServiceCollection(); services.AddLogging(); services.AddWorkflow(); services.AddTransient<GoodbyeWorld>(); var serviceProvider = services.BuildServiceProvider(); var host = serviceProvider.GetService<IWorkflowHost>(); host.RegisterWorkflow<HelloWorldWorkflow>(); host.Start(); host.StartWorkflow("HelloWorld"); Console.ReadLine(); host.Stop();
|
案例2 传递数据
1 2 3 4 5 6
| public class MyDataClass { public int Value1 { get; set; } public int Value2 { get; set; } public int Value3 { get; set; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class AddNumbers : StepBodyAsync { public int Input1 { get; set; } public int Input2 { get; set; } public int Output { get; set; }
public override async Task<ExecutionResult> RunAsync(IStepExecutionContext context) { Output = (Input1 + Input2); return ExecutionResult.Next(); } }
public class CustomMessage : StepBody { public string Message { get; set; } public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine(Message); return ExecutionResult.Next(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class PassingDataWorkflow : IWorkflow<MyDataClass> { public void Build(IWorkflowBuilder<MyDataClass> builder) { builder .StartWith(context => { Console.WriteLine("Starting workflow..."); return ExecutionResult.Next(); }) .Then<AddNumbers>() .Input(step => step.Input1, data => data.Value1) .Input(step => step.Input2, data => data.Value2) .Output(data => data.Value3, step => step.Output) .Then<CustomMessage>() .Name("Print custom message") .Input(step => step.Message, data => "The answer is " + data.Value3.ToString()) .Then(context => { Console.WriteLine("Workflow complete"); return ExecutionResult.Next(); }); } public string Id => "PassingDataWorkflow"; public int Version => 1; }
public class PassingDataWorkflow2 : IWorkflow<Dictionary<string, int>> { public void Build(IWorkflowBuilder<Dictionary<string, int>> builder) { builder .StartWith(context => { Console.WriteLine("Starting workflow..."); return ExecutionResult.Next(); }) .Then<AddNumbers>() .Input(step => step.Input1, data => data["Value1"]) .Input(step => step.Input2, data => data["Value2"]) .Output((step, data) => data["Value3"] = step.Output) .Then<CustomMessage>() .Name("Print custom message") .Input(step => step.Message, data => "The answer is " + data["Value3"].ToString()) .Then(context => { Console.WriteLine("Workflow complete"); return ExecutionResult.Next(); }); }
public string Id => "PassingDataWorkflow2";
public int Version => 1;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| IServiceCollection services = new ServiceCollection(); services.AddLogging(); services.AddWorkflow(); var serviceProvider = services.BuildServiceProvider(); var host = serviceProvider.GetService<IWorkflowHost>(); host.RegisterWorkflow<PassingDataWorkflow, MyDataClass>(); host.RegisterWorkflow<PassingDataWorkflow2, Dictionary<string, int>>(); host.Start(); var initialData = new MyDataClass { Value1 = 2, Value2 = 3 };
host.StartWorkflow("PassingDataWorkflow", 1, initialData); var initialData2 = new Dictionary<string, int> { ["Value1"] = 7, ["Value2"] = 2 };
host.StartWorkflow("PassingDataWorkflow2", 1, initialData2);
Console.ReadLine(); host.Stop();
|