如何为Windows Workflow 4状态机创建内存持久化存储并恢复实例?
如何在Windows Workflow 4中基于自定义状态类恢复状态机实例
我完全理解你的需求:想要绕开WF4默认的严格序列化机制,用自己的轻量状态类(比如你的StateObject)直接恢复状态机到指定状态,同时保留接口注入服务的能力,还要避免那种在开头加状态跳转的别扭做法。下面是几个可行的实践方向,结合代码示例帮你实现目标:
核心思路:自定义轻量持久化 + 状态机"定向唤醒"
WF4状态机的核心是通过Bookmark实现挂起和恢复,但默认持久化会序列化整个实例。我们可以换个思路:只保存你关心的关键状态(就是StateObject里的内容),恢复时直接创建新的状态机实例,通过Bookmark直接推进到目标状态,而非反序列化旧实例。
1. 改造状态机:给每个状态添加可唤醒入口
首先,让状态机的每个业务状态都拥有一个专属的Bookmark,同时支持接收StateObject作为输入参数,用来恢复内部变量。示例代码如下:
public class MyStateMachine : StateMachine { // 注入的服务接口 public InArgument<IMyService> MyService { get; set; } // 自定义状态跟踪对象 public InArgument<StateObject> StateTracker { get; set; } public MyStateMachine() { // 定义业务状态 var pendingState = new State { DisplayName = "Pending" }; var approvedState = new State { DisplayName = "Approved" }; var rejectedState = new State { DisplayName = "Rejected" }; // Pending状态:创建Bookmark作为恢复入口,同时使用StateTracker恢复变量 pendingState.Entry = new Sequence { Activities = { new CreateBookmark { Name = "Resume_Pending" }, new WriteLine { Text = new InArgument<string>(ctx => $"恢复到Pending状态,当前值:{StateTracker.Get(ctx).Value}") }, // 这里添加Pending状态的业务逻辑,可注入MyService使用 } }; // Approved状态同理 approvedState.Entry = new Sequence { Activities = { new CreateBookmark { Name = "Resume_Approved" }, new WriteLine { Text = new InArgument<string>(ctx => $"恢复到Approved状态,更新日期:{StateTracker.Get(ctx).LastUpdated:yyyy-MM-dd}") } } }; // 配置状态转换(根据你的业务需求定义) this.InitialState = pendingState; this.States.Add(pendingState); this.States.Add(approvedState); this.States.Add(rejectedState); } }
2. 实现自定义持久化存储
创建你的InMemoryPersistentStore,只存储StateObject和目标状态名称,完全不需要序列化整个工作流:
public class InMemoryPersistentStore { private StateObject _savedState; private string _targetStateName; public InMemoryPersistentStore(StateObject initialState) { _savedState = initialState; _targetStateName = initialState.State; } // 挂起时调用:保存当前状态和状态名称 public void SaveCurrentState(StateObject currentState, string stateName) { _savedState = currentState; _targetStateName = stateName; } // 获取保存的状态信息 public (StateObject State, string TargetStateName) GetSavedState() => (_savedState, _targetStateName); }
3. 实现恢复逻辑:创建实例 + 定向唤醒目标状态
通过WorkflowFactory来封装恢复流程,同时处理服务注入:
public class WorkflowFactory { private readonly IServiceProvider _serviceProvider; // 注入服务容器,用来提供状态机需要的服务实例 public WorkflowFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public WorkflowInstance ResumeFrom(InMemoryPersistentStore store) { var (savedState, targetState) = store.GetSavedState(); // 1. 创建新的状态机实例 var stateMachine = new MyStateMachine(); // 2. 注入所需服务和状态跟踪对象 var inputs = new Dictionary<string, object> { { nameof(MyStateMachine.MyService), _serviceProvider.GetService<IMyService>() }, { nameof(MyStateMachine.StateTracker), savedState } }; // 3. 创建WorkflowApplication并定向唤醒目标状态的Bookmark var workflowApp = new WorkflowApplication(stateMachine, inputs); workflowApp.ResumeBookmark($"Resume_{targetState}", null); return new WorkflowInstance(workflowApp); } } // 简单封装WorkflowApplication,对外暴露业务方法 public class WorkflowInstance { private readonly WorkflowApplication _app; public WorkflowInstance(WorkflowApplication app) { _app = app; } public void DoSomething() { // 继续执行状态机的业务逻辑 _app.Run(); } }
4. 使用示例
完全符合你期望的调用方式:
// 初始化存储(模拟从持久化介质加载的状态) var store = new InMemoryPersistentStore(new StateObject() { State = "Pending", Value = 10 }); // 通过工厂恢复实例 var serviceProvider = new ServiceCollection() .AddScoped<IMyService, MyService>() .BuildServiceProvider(); var factory = new WorkflowFactory(serviceProvider); var workflowInstance = factory.ResumeFrom(store); // 执行恢复后的业务逻辑 workflowInstance.DoSomething();
关键优势说明
- 避免默认序列化的繁琐:只保存你关心的简单类型状态,不需要处理整个工作流实例的序列化问题,也不存在版本兼容风险。
- 符合状态机设计理念:通过Bookmark直接唤醒目标状态的入口逻辑,不是先进入初始状态再跳转,完全契合状态机的状态驱动设计。
- 灵活的依赖注入:每次恢复都创建新的状态机实例,通过
IServiceProvider注入所需服务,完美支持接口注入模式。
关于StateMachineStateTracker的替代
你提到的StateMachineStateTracker确实依赖Sql持久化且文档匮乏,它的核心思路其实和上面的方案类似,但被绑定到了Sql存储上。我们自己实现的方案更灵活,可适配任何存储介质(内存、文件、自定义数据库等),也不需要处理复杂的序列化约束。
内容的提问来源于stack exchange,提问作者The Senator
相关产品推荐
相关产品推荐

