基础概念
步骤(step)
工作流由一系列相连的步骤组成,每个步骤都可以有输入和输出,这些输出也可以传递到它所在的工作流。可以通过StepBody
和StepBodyAsync
抽象类来创建步骤并实现Run/RunAsync
方法,该方法也可以在定义工作流的时候内联创建。
public class HelloWorld : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Hello world"); return ExecutionResult.Next(); } }
StepBody
和StepBodyAsync
是由WrokflowCore宿主创建,在创建的时候首先尝试使用IServiceProvider进行依赖注入,如果无法使用则搜索无参构造函数。
工作流
通过组成一系列步骤来组成工作流,通过实现IWorkflow接口来实现
public class HelloWorldWorkflow : IWorkflow { public string Id => "HelloWorld"; public int Version => 1; public void Build(IWorkflowBuilder<object> builder) { builder .StartWith<HelloWorld>() .Then<GoodbyeWorld>(); } }
使用内联方式定义
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启动特定工作流。
var host = serviceProvider.GetService<IWorkflowHost>(); host.RegisterWorkflow<HelloWorldWorkflow>(); host.Start(); host.StartWorkflow("HelloWorld", 1, null); Console.ReadLine(); host.Stop();
步骤之间传递参数
//带有输入和输出参数的步骤 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容器注入步骤,工作流则会使用容器来注入步骤的依赖项
需要注入依赖的服务:
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 基本使用
steps
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(); } }
workflow
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; }
program
IServiceCollection services = new ServiceCollection(); services.AddLogging(); services.AddWorkflow(); services.AddTransient<GoodbyeWorld>();//因为GloodbyeWorld中使用了依赖注入,所以需要向容器中进行注册 var serviceProvider = services.BuildServiceProvider(); var host = serviceProvider.GetService<IWorkflowHost>(); host.RegisterWorkflow<HelloWorldWorkflow>(); host.Start(); host.StartWorkflow("HelloWorld"); Console.ReadLine(); host.Stop();
案例2 传递数据
DataClass
public class MyDataClass { public int Value1 { get; set; } public int Value2 { get; set; } public int Value3 { get; set; } }
steps
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(); } }
workflow
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)//将Input1和data.Value1进行绑定 .Input(step => step.Input2, data => data.Value2) .Output(data => data.Value3, step => step.Output)//将step.Output赋值给data.Value3 .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; }
program
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 }; //使用PassingDataWorkflow host.StartWorkflow("PassingDataWorkflow", 1, initialData); var initialData2 = new Dictionary<string, int> { ["Value1"] = 7, ["Value2"] = 2 }; //使用PassingDataWorkflow2 host.StartWorkflow("PassingDataWorkflow2", 1, initialData2); Console.ReadLine(); host.Stop();