基本概念
步骤
工作流由一系列连接的步骤组成。每个步骤都产生一个结果值,后续步骤由订阅前面步骤的特定结果触发。null的默认结果可用于基本的线性工作流。步骤通常是通过继承StepBody或StepBodyAsync抽象类并实现Run/RunAsync方法来定义的。还可以在定义工作流结构时内联创建它们。
首先,我们定义一些步骤
public class HelloWorld : StepBody
{
public override ExecutionResult Run(IStepExecutionContext context)
{
Console.WriteLine("Hello world");
return ExecutionResult.Next();
}
}
StepBody和StepBodyAsync类实现是由工作流主机构造的,它首先尝试使用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>();
}
}
IWorkflow接口还有一个只读Id属性和只读版本属性。它们通常是静态的,工作流主机使用它们来标识工作流定义。
您还可以内联定义您的步骤
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();
});
}
}
在每个步骤之间,每个正在运行的工作流都被持久化到所选择的持久性提供者,可以在稍后的某个时间点提取工作流以继续执行。步骤的结果可以指示工作流主机将工作流的进一步执行推迟到将来的某个时间点或响应外部事件。
第一次调用工作流中的特定步骤时,上下文对象上的PersistenceData属性为null。Run方法产生的ExecutionResult可以通过提供一个结果值使工作流进入下一个步骤,指示工作流休眠一段已定义的时间,或者干脆不将工作流向前移动。如果没有产生结果值,那么通过设置PersistenceData,该步骤将成为可重入的,因此工作流主机将在将来再次调用这个步骤,buy将用它之前的值填充PersistenceData。
例如,此步骤最初将使用null PersistenceData运行,并将工作流休眠12个小时,同时将PersistenceData设置为new Object()。12小时后,该步骤将被再次调用,但上下文。PersistenceData现在将包含在前一个迭代中构造的对象,并将产生一个空的结果值,从而导致工作流向前移动。
public class SleepStep : StepBody
{
public override ExecutionResult Run(IStepExecutionContext context)
{
if (context.PersistenceData == null)
return ExecutionResult.Sleep(Timespan.FromHours(12), new Object());
else
return ExecutionResult.Next();
}
}
主机
工作流主机是负责执行工作流的服务。它通过轮询持久性提供者来实现这一点,即轮询准备运行的工作流实例,执行它们,然后将它们传递回持久性提供者,以备下一次运行时使用。它还负责将事件发布到可能正在等待它的任何工作流。
设置
使用IServiceCollection的AddWorkflow扩展方法来配置应用程序启动时的工作流主机。默认情况下,它被配置为MemoryPersistenceProvider和SingleNodeConcurrencyProvider来进行测试。此时,您还可以配置一个DB持久性提供程序。
services.AddWorkflow();
使用
当您的应用程序启动时,从内置的依赖注入框架IServiceProvider中获取工作流主机。确保调用RegisterWorkflow,以便工作流主机知道所有工作流,然后调用Start()来启动执行工作流的线程池。使用StartWorkflow方法启动特定工作流的新实例。
var host = serviceProvider.GetService<IWorkflowHost>();
host.RegisterWorkflow<HelloWorldWorkflow>();
host.Start();
host.StartWorkflow("HelloWorld", 1, null);
Console.ReadLine();
host.Stop();
在步骤中注入依赖项
说明了依赖注入在工作流步骤中的使用。
考虑以下服务
public interface IMyService
{
void DoTheThings();
}
...
public class MyService : IMyService
{
public void DoTheThings()
{
Console.WriteLine("Doing stuff...");
}
}
以下哪个是由工作流步骤消耗的
public class DoSomething : StepBody
{
private IMyService _myService;
public DoSomething(IMyService myService)
{
_myService = myService;
}
public override ExecutionResult Run(IStepExecutionContext context)
{
_myService.DoTheThings();
return ExecutionResult.Next();
}
}
在设置IoC容器时,只需同时添加服务和工作流步骤作为服务集合的过渡。
IServiceCollection services = new ServiceCollection();
services.AddLogging();
services.AddWorkflow();
services.AddTransient<DoSomething>();
services.AddTransient<IMyService, MyService>();