基础概念

步骤(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();