惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
Recorded Future
Recorded Future
人人都是产品经理
人人都是产品经理
S
SegmentFault 最新的问题
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
量子位
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tor Project blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
T
Threatpost
T
Tenable Blog
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
C
Cisco Blogs
H
Heimdal Security Blog
Attack and Defense Labs
Attack and Defense Labs
A
About on SuperTechFans
Last Week in AI
Last Week in AI
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
I
Intezer
V
V2EX
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
V
Vulnerabilities – Threatpost
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
PCI Perspectives
PCI Perspectives
P
Privacy International News Feed
D
Docker

博客园 - Hey,Coder!

Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 Ocelot + Consul + SignalR 粘性会话 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard docker consul registrator 服务自动注册consul 微服务动态扩容 ubuntu24.04 安装docker RAG 检索增强生成 软考 - 架构设计师 知识点总结 c# MailKit3.4.3 发送邮件、附件 nginx 反向代理postgresql c# 信号量 elsa 3.5 中间件记录最后执行的节点数据 xp密钥 c# System.Text.Json 反序列化Dictionary<string,object>时未转换基础类型的处理方法 docker配置代理 openclaw 接入 LMStudio的模型服务 wpf canvas 移动 缩放 windows平台openclaw搭建 wpf scrollerview触摸滚动 c# 动态切换sqlsugar连接字符串 c# Elastic.Clients.Elasticsearch 动态查询 c# es 封装Elastic.Clients.Elasticsearch c# scrollerview滚动到指定元素位置 WPF 重写Expander 基于elsa工作流封装一套变量、组件的体系 常用活动重写 DynamicExpresso 轻量级动态表达式库 leaflet docker 连接老版本的sqlserver ssl错误 c# quartz 动态创建任务 控制任务运行和停止 依赖注入 cefsharp 模拟点击 批量重置数据库的数据 docker 复制远程镜像本地并创建容器 c# newtonsoft dynamic
c# elsa 3.5.2 程序化工作流常用功能及自定义中间件
Hey,Coder! · 2026-01-21 · via 博客园 - Hey,Coder!

nuget

Elsa 3.5.3

依赖注入

builder.Services.AddElsa();

变量保存

如果具体版本中无法使用此逻辑,可以通过workflow的meta字段存储,并在中间件中读取执行结束时的所有变量并保存,在恢复执行时重新创建参数注入即可

var variable = new Variable(paraName, obj);
//将变量保存在流程的持久化数据中
variable.WithWorkflowStorage();

variable.WithMemoryStorage();
variable.WithStorage();

物化流程

如果需要使用elsa提供的方法则需要保证流程正确的保存,保存后就可以用便捷的方式恢复流程继续运行

//主要使用这些方法
// IWorkflowDefinitionStore s;
 //s.SaveAsync();
 //IWorkflowDefinitionPublisher p;
// p.PublishAsync

//IWorkflowRegistry 注册工作流

保存流程


var flow=new WorkFlow();

// 3. 创建或获取工作流定义
var workflowDefinition = new WorkflowDefinition
{
    DefinitionId = flow.DefinitionHandle.DefinitionId,
    Id = Guid.NewGuid().ToString("N"),
    //Name = workflowDefinition.Name,
    //Description = workflowDefinition.Description,
    //Version = flow.DefinitionHandle.VersionOptions.Value + 1,
    CreatedAt = DateTimeOffset.UtcNow
};

// 4. 设置工作流定义属性
workflowDefinition.MaterializerName = "Json";
//workflowDefinition.StringData = rootActivityJson; // 存储序列化数据
workflowDefinition.IsLatest = true;
workflowDefinition.IsPublished = false; // 先保存为草稿

//物化流程
var store = serviceProvider.GetService<IWorkflowDefinitionStore>();
store.SaveAsync(workflowDefinition);

发布流程

//发布流程
  var publisher = serviceProvider.GetService<IWorkflowDefinitionPublisher>();
  publisher.PublishAsync(flow.DefinitionHandle.DefinitionId);

物化流程后的执行和恢复


   var workflowRuntime = serviceProvider.GetService<IWorkflowRuntime>();
   var workflowDefinitionId = "f9f9adebad8a4f6bb63fc4b50aabcfb1";

   //var workflowInstance = await _workflowInstanceStore.FindAsync(filter);
   //options.BookmarkId = workflowInstance.WorkflowState.Bookmarks.First().Id;


   // 1. 创建通用客户端(无参数)
   var workflowClient = await workflowRuntime.CreateClientAsync(cancellationToken: CancellationToken.None);

   // 2. 使用 CreateAndRunInstanceAsync 启动工作流
   var startRequest = new CreateAndRunWorkflowInstanceRequest
   {
       // 关键:通过 WorkflowDefinitionHandle 指定要启动的工作流定义
       WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId),

       // 可选:指定特定版本
       // WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionVersionId(workflowDefinition.Id),

       // 传入输入参数
       //Input = new
       //{
       //    OrderId = 12345,
       //    UserName = "张三",
       //    // 或者直接使用您的 para 字典
       //    // Data = para
       //},

       // 关联ID(用于业务追踪)
       CorrelationId = "ORDER-12345",

       // 实例名称
       Name = "订单处理流程",

       // 父工作流ID(如果是子工作流)
       // ParentId = parentInstanceId,

       // 附加属性
       Properties = new Dictionary<string, object>
       {
           ["Source"] = "API调用",
           ["CreatedBy"] = "System"
       }
   };

   // 3. 执行启动
   var runResult = await workflowClient.CreateAndRunInstanceAsync(startRequest);


var workflowInstanceStore = serviceProvider.GetService<IWorkflowInstanceStore>();
var options = new RunWorkflowOptions();
options.WorkflowInstanceId = request.InstanceId;
var filter = new Elsa.Workflows.Management.Filters.WorkflowInstanceFilter();
filter.Id = request.InstanceId;


//恢复执行
var workflowInstance = await workflowInstanceStore.FindAsync(filter);
var bookmarkId = workflowInstance.WorkflowState.Bookmarks.First().Id;

var existingInstanceId = request.InstanceId;
var workflowRuntime = serviceProvider.GetService<IWorkflowRuntime>();
var workflowDefinitionId = "f9f9adebad8a4f6bb63fc4b50aabcfb1";
// 1. 为特定实例创建客户端(传入实例ID)
var instanceClient = await workflowRuntime.CreateClientAsync(
    workflowInstanceId: existingInstanceId,  // 已存在的工作流实例ID
    cancellationToken: CancellationToken.None);

// 2. 使用 RunInstanceAsync 恢复执行
var resumeRequest = new RunWorkflowInstanceRequest
{
    // 必须指定要恢复的书签ID
    BookmarkId = bookmarkId,
    // 恢复时传入的数据
    //Input = new
    //{
    //    ApprovalResult = "Approved",
    //    Approver = "李四",
    //    Timestamp = DateTime.UtcNow
    //},

    // 可选:指定恢复的活动(如果知道具体活动)
    // ActivityHandle = ActivityHandle.ByActivityId("activity-id"),

    // 可选:关联ID
    //CorrelationId = "RESUME-ORDER-12345",

    // 附加属性
    Properties = new Dictionary<string, object>
    {
        ["ResumedAt"] = DateTime.UtcNow,
        ["ResumedBy"] = "审批系统"
    }
};

// 3. 执行恢复
var resumeResult = await instanceClient.RunInstanceAsync(resumeRequest);



//恢复流程
var _workflowResumer = serviceProvider.GetService<IWorkflowResumer>();
await _workflowResumer.ResumeAsync(options.BookmarkId, para);

事件订阅

常用事件

  • 工作流生命周期事件:
    • WorkflowStarted:工作流启动时触发
    • WorkflowExecuting:工作流执行中触发
    • WorkflowExecuted:工作流执行完成触发
    • WorkflowFinished:工作流结束时触发
  • 活动执行事件:
    • ActivityExecuting:活动开始执行时触发
    • ActivityExecuted:活动执行完成时触发
    • ActivityCancelled:活动被取消时触发
  • 工作流定义管理事件:
    • WorkflowDefinitionCreated:创建工作流定义时触发
    • WorkflowDefinitionPublished:发布工作流定义时触发
    • WorkflowDefinitionRetracted:撤回工作流定义时触发
    public class WorkflowStartedHandler :  INotificationHandler<WorkflowStarted>
    {

        public Task HandleAsync(WorkflowStarted notification, CancellationToken cancellationToken)
        {

            var workflowInstance = notification.WorkflowExecutionContext;
            Console.WriteLine($"工作流 {workflowInstance.Name} (实例 {workflowInstance.Id}) 已开始");
            return Task.CompletedTask;
        }
    }

//注册事件
builder.Services.AddNotificationHandlersFrom<WorkflowStartedHandler>();

基础流程创建

//需要注入对象
IWorkflowRunner workflowRunner;

//顺序工作流

var workflow = new Sequence();

workflow.Activities = new List<IActivity>();

for (int i = 0; i < 3; i++)
{
    workflow.Activities.Add(new WriteLine("test"));
}

var result = await workflowRunner.RunAsync(workflow);

//流程

var nameVariable = new Variable<string>();

// Define the activities to put in the flowchart:
var writeLine1 = new WriteLine("Please tell me your name:");
//var writeLine2 = new ReadLine(nameVariable);
var writeLine2 = new WriteLine(nameVariable);
var writeLine3 = new WriteLine(context => $"Nice to meet you, {nameVariable.Get(context)}!");
var cus = new TestActivity();

// Define a flowchart workflow:
var workflow = new Flowchart
{
    // Register the name variable.
    Variables = { nameVariable },

    // Add the activities.
    Activities =
    {
        writeLine1,
        writeLine2,
        writeLine3,
        cus
    },

    // Setup the connections between activities.
    Connections =
    {
        new Connection(writeLine1, writeLine2),
        new Connection(writeLine2, writeLine3),
        new Connection(writeLine3, cus)
    }
};

var result = await workflowRunner.RunAsync(workflow);

自定义活动

注册自定义活动

builder.Services.AddElsa(options => options.AddActivity<TestActivity>());

自定义活动

通过context.CompleteActivityAsync标记当前活动已经完成,可以直接执行后续的活动

   public class TestActivity : Activity //, IActivityWithResult
   {
       //public Output Result { get; set; } = new();

       //[Input] public Input<decimal> Amount { get; set; } = default!;
       //[Output] public Output<string> TransactionId { get; set; } = default!;

       /// <summary>
       /// 判断活动是否可以执行
       /// </summary>
       /// <param name="context"></param>
       /// <returns></returns>
       protected override ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context)
       {
           //Console.WriteLine("CanExecuteAsync!");
           return ValueTask.FromResult(true);
           //return base.CanExecuteAsync(context);
       }

       /// <summary>
       /// 活动中执行的具体逻辑
       /// </summary>
       /// <param name="context"></param>
       /// <returns></returns>
       protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
       {
           Console.WriteLine("活动已执行!");

           return context.CompleteActivityAsync();
       }
   }

变量定义、获取、修改

在定义工作流时定义变量或者在活动节点内部定义

工作流定义变量

通过Variables属性定义工作流的变量,也可以在后续节点中进行定义

var nameVariable = new Variable<bool>("boolid", true);
var testStrVariable = new Variable<string>("testStr", "testStr");

Input<bool> x = new(false);

//顺序工作流
var workflow = new Sequence();
workflow.Variables = new List<Variable>()
{
    nameVariable,
    testStrVariable
};

活动内对变量进行操作

主要是通过上下文对象的几个变量操作的方法进行处理

 protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
 {
     Console.WriteLine("活动已执行!");

     //旧版本写法
     //var nameVariable = new Variable<bool>("boolid", false);
     ////nameVariable.Value = true;
     //var x = nameVariable.Get(context);
     //var testVariable = new Variable<string>("testStr", string.Empty);
     //var strValue = testVariable.Get(context);


     var boolValue = context.GetVariable<bool>("boolid");
     var strValue = context.GetVariable<string>("testStr");

     //设置变量值
     context.SetVariable("testStr", "act-test");

     //添加一个新变量
     context.SetVariable("newVar", "newVar");
      
     //获取修改后的变量
     strValue = context.GetVariable<string>("testStr");


     //var testMetadata = context.GetMetadata<string>("testMetadata");

     return context.CompleteActivityAsync();
 }

获取内部依赖注入

var service = context.GetRequiredService<FlowService>();

入参

传入入参

通过RunWorkflowOptions的Input属性传递入参

var option = new RunWorkflowOptions();
option.Input = new Dictionary<string, object>();
option.Input.Add("inputStr1", "inputStr1");

var result = await workflowRunner.RunAsync(workflow, option);

读取入参

在活动节点内部通过上下文的WorkflowInput属性获取

protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
    var inputStr = context.WorkflowInput["inputStr1"];

    return context.CompleteActivityAsync();
}

出参

活动内部写入出参

protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
    context.WorkflowExecutionContext.Output.Add("outputStr1", "outputstr");
    context.WorkflowExecutionContext.Output.Add("outputStr2", "outputstr2");

    return context.CompleteActivityAsync();
}

获取出参结果

var workflow = ElsaUtil.GetVaraibleTest();
workflow.Name = "Test";
workflow.Id = "testFlowID";

var option = new RunWorkflowOptions();


var result = await workflowRunner.RunAsync(workflow, option);

//获取结果
var output = result.WorkflowState.Output;

IWorkflowRuntime

说明

IWorkflowRuntime提供了更多高级属性和功能,workflowRunner只提供了基础的功能。
如果希望由触发器触发、消息队列触发工作流时可以用这种方式。
可以参考官方说明IWorkflowRunner vs IWorkflowRuntime vs IWorkflowDispatcher
IWorkflowRunner

  • 为工作流程逻辑编写单元测试
  • 执行不需要持久化的简单、短暂的工作流程
  • 完全在进程中运行工作流程,无需外部依赖
  • 你需要即时、同步执行

IWorkflowRuntime

  • 构建需要工作流持久性和状态管理的应用程序
  • 暂停后你需要恢复工作流程(书签、延迟)。
  • 你需要用于工作流程作的高级客户端 API
  • 大多数生产场景中,执行要求标准

基础使用

  1. 创建测试流程
public class TestFlow : WorkflowBase
{
    protected override void Build(IWorkflowBuilder builder)
    {
        builder.Root = new WriteLine("Hello flow!");
    }
}
  1. 注册流程
builder.Services.AddElsa(elsa =>
    {
        elsa.AddWorkflow<TestFlow>();
    }
);
  1. 注入IWorkflowRuntime
    根据配置的注入方式在使用的地方注入
IWorkflowRuntime workflowRuntime;
  1. 调用
var client = await workflowRuntime.CreateClientAsync();

var result = await client.CreateAndRunInstanceAsync(new CreateAndRunWorkflowInstanceRequest
{
    WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId("TestFlow"),
    Input = new Dictionary<string, object>
    {
        ["message"] = "Hello from the library!",
        ["userId"] = 123
    },
    CorrelationId = "optional-correlation-id",
    TriggerActivityId = "testFlowID"
});

CodeActivity

前面通过继承Activity可以实现自定义活动,但是需要调用return context.CompleteActivityAsync();通过引擎当前活动已经执行结束可以继续下一个活动。
也可以通过CodeActivity来实现,CodeActivity继承了Activity,可以用来处理简单的有明确输入输出的活动。
Activity可以用来处理复杂的场景,控制暂停和恢复。CodeActivity通常一次执行完成。

支持泛型参数CodeActivity

public class TestCodeActivity : CodeActivity
{
    /// <summary>
    /// 
    /// </summary>
    public Input<string> TestName { get; set; } = default!;


    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    protected override ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context)
    {
        return ValueTask.FromResult(true);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
    {
        Console.WriteLine($"活动已执行!{TestName}");

        return ValueTask.CompletedTask;
    }
}

获取CodeActivity返回值

  1. 定义返回值类型(CodeActivity)
public class TestCodeGenericActivity : CodeActivity<string>
{

    protected override ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context)
    {
        return ValueTask.FromResult(true);
    }

    protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
    {
        context.SetResult("ExecuteAsync-Result");
        return ValueTask.CompletedTask;
    }
}
  1. 调用
public static IActivity TestResult()
{
    var testStrVariable = new Variable<string>("testStr", "testStr");

    var workflow = new Sequence();
    workflow.Variables = new List<Variable>()
    {
        testStrVariable
    };

    workflow.Activities = new List<IActivity>();

    var resultActivity = new TestCodeGenericActivity()
    {
        Result = new(testStrVariable)
    };
    workflow.Activities.Add(resultActivity);
    workflow.Activities.Add(new WriteLine(context => $"问候语是:{testStrVariable.Get(context)}"));

    return workflow;
}

书签

可以实现先暂停任务,等待外部触发逻辑后继续执行

  1. 创建书签
    书签就是在活动中通过context.CreateBookmark();创建书签,并在流程的返回状态中获取书签id。
    后续可以通过书签id继续执行流程。
public class BookmarkTest : Activity
{
    protected override void Execute(ActivityExecutionContext context)
    {
        // 创建一个书签。创建的书签将存储在工作流状态中。
        context.CreateBookmark();

        // 此活动在事件发生之前不会完成。
    }
}
  1. 注册
elsa.AddActivity<BookmarkTest>();
  1. 调用
var workflow = new Workflow
{
    Root = new Sequence
    {
        Activities =
        {
            new WriteLine("工作流开始..."),
            new BookmarkTest(), // 这将会阻塞后续执行,直到 MyEvent 的书签被恢复。
            new WriteLine("事件发生!")
        }
    }
};

workflow.Name = "Test";
workflow.Id = "testFlowID";

var result = await workflowRunner.RunAsync(workflow);
  1. 继续执行
    调用完成后将书签id、实例id、WorkflowState保存后继续执行。
    如果需要更实用的示例,可以看后续的持久化的示例
var instanceID = result.WorkflowExecutionContext.Id;

var workflowState = result.WorkflowState;
var bookmark = workflowState.Bookmarks.Single(); // 获取由 MyEvent 活动创建的书签。
var options = new RunWorkflowOptions();
options.BookmarkId = bookmark.Id;

options.WorkflowInstanceId = instanceID;


// 恢复工作流。
//await workflowRunner.RunAsync(workflow, options);

//内存缓存时可以不传递state,持久化时需要传递否则无法执行标签后续的活动
//await workflowRunner.RunAsync(workflow, options);
await workflowRunner.RunAsync(workflow, workflowState, options);

流程保存成json后加载执行

注入序列化器

IActivitySerializer activitySerializer;

加载并执行到书签后继续执行

var workflowRaw = ElsaUtil.TestBookmark();
workflowRaw.Name = "Test";
workflowRaw.Id = "testFlowID";

var json = activitySerializer.Serialize(workflowRaw);
var workflow = activitySerializer.Deserialize<Workflow>(json);


var result = await workflowRunner.RunAsync(workflow);

var instanceID = result.WorkflowExecutionContext.Id;

var workflowState = result.WorkflowState;
var bookmark = workflowState.Bookmarks.Single();
var options = new RunWorkflowOptions();
options.BookmarkId = bookmark.Id;

options.WorkflowInstanceId = instanceID;


// 恢复工作流。
await workflowRunner.RunAsync(workflow, result.WorkflowState, options);

持久化

使用pg,需要安装两个nuget包【Elsa.EntityFrameworkCore】、【Elsa.EntityFrameworkCore.PostgreSql】

  1. 不显示ef执行log
builder.Services.AddLogging(logging =>
{
    logging.ClearProviders();
    logging.AddConsole();
    logging.AddDebug();

    // 过滤掉 EF Core 的 SQL 日志
    logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning);
    logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Connection", LogLevel.Warning);
    logging.AddFilter("Microsoft.EntityFrameworkCore.Infrastructure", LogLevel.Warning);
    logging.AddFilter("Microsoft.EntityFrameworkCore.Query", LogLevel.Warning);
});
  1. 注入ef的配置
builder.Services.AddElsa(elsa =>
    {
        var connectionString = "Host=127.0.0.1;Port=5432;Database=dbName;Username=dev;Password=testPassword;SearchPath=public";

        // 配置管理层以使用EF Core。
        elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef =>
        ef.UsePostgreSql(connectionString)));
        elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(ef =>
            ef.UsePostgreSql(connectionString)));
    }
);
  1. 创建流程
var workflow = new Workflow
{
    Root = new Sequence
    {
        Activities =
        {
            new WriteLine("工作流开始..."),
            new BookmarkTest(), // 这将会阻塞后续执行,直到 MyEvent 的书签被恢复。
            new WriteLine("事件发生!")
        }
    }
};

var result = await workflowRunner.RunAsync(workflow);
var bookmarkID = result.WorkflowState.Bookmarks.First().Id;
var instanceID = result.WorkflowExecutionContext.Id;
  1. 注入实例存储的对象
private readonly IWorkflowInstanceStore _workflowInstanceStore;
  1. 搜索旧的流程并执行
var workflow = new Workflow
{
    Root = new Sequence
    {
        Activities =
        {
            new WriteLine("工作流开始..."),
            new BookmarkTest(), // 这将会阻塞后续执行,直到 MyEvent 的书签被恢复。
            new WriteLine("事件发生!")
        }
    }
};

//查找流程实例获取状态属性
var filter = new Elsa.Workflows.Management.Filters.WorkflowInstanceFilter();
filter.Id = instanceID;

var workflowInstance = await _workflowInstanceStore.FindAsync(filter);

// 恢复工作流,需要WorkflowState才能正确执行
await workflowRunner.RunAsync(workflow, workflowInstance.WorkflowState, options);
  1. 备注
    如果需要使用FlowChart时恢复流程,可以使用Workflow将flowChart包在内部即可
 var workflow = new Flowchart
 {
     Activities =
     {
         new WriteLine("工作流开始..."),
         new BookmarkTest(), // 这将会阻塞后续执行,直到 MyEvent 的书签被恢复。
         new WriteLine("事件发生!")
     }
 };

var wf = new Workflow
{
    Root = workflow
};

var result = await workflowRunner.RunAsync(wf);
var instanceID = result.WorkflowExecutionContext.Id;

var options = new RunWorkflowOptions();
options.WorkflowInstanceId = instanceID;
var filter = new Elsa.Workflows.Management.Filters.WorkflowInstanceFilter();
filter.Id = instanceID;

var workflowInstance = await _workflowInstanceStore.FindAsync(filter);
options.BookmarkId = workflowInstance.WorkflowState.Bookmarks.First().Id;
options.WorkflowInstanceId = instanceID;

await workflowRunner.RunAsync(wf, workflowInstance.WorkflowState, options);

自定义中间件

中间件

public class WorkflowCompletionMiddleware : IWorkflowExecutionMiddleware
{
    private readonly WorkflowMiddlewareDelegate _next;

    public WorkflowCompletionMiddleware(WorkflowMiddlewareDelegate next)
    {
        this._next = next;
    }

    public async ValueTask InvokeAsync(WorkflowExecutionContext context)
    {
        // 调用下一个中间件
        await _next(context);

       //记录流程执行结果
       if (context.Status == WorkflowStatus.Finished)
       {
           var vari = context.Workflow.Variables.ToList();
           var result = context.Variables.ToList();
           //var testStr = context.Variables.GetPropertyValue<string>("testStr");
           //通过容器获取依赖注入的类型
           //var sugar = context.GetService<ISqlSugarClient>();
       }
    }
}

注册中间件

//在原有在AddElsa中通过UseWorkflows调用WithWorkflowExecutionPipeline注册
builder.Services.AddElsa(elsa =>
{
    elsa.UseWorkflows(workflows =>
    {
        workflows.WithWorkflowExecutionPipeline(pipeline =>
        {
            //pipeline.UseMiddleware<WorkflowCompletionMiddleware>();
            //pipeline.UseDefaultPipeline();
            //重写UseDefaultPipeline
            pipeline.Reset()
            .UseMiddleware<WorkflowCompletionMiddleware>()
            .UseWorkflowHeartbeat()
            .UseEngineExceptionHandling()
            .UsePersistentVariables()
            .UseExceptionHandling()
            .UseDefaultActivityScheduler();

        });
    });
}
);

中间件获取执行状态

可以通过WorkflowExecutionContext类型的Status、SubStatus字段获取。类型定义如下

public enum WorkflowSubStatus
{
    //
    // 摘要:
    //     The workflow is pending execution.
    Pending,
    //
    // 摘要:
    //     The workflow is currently executing.
    Executing,
    //
    // 摘要:
    //     The workflow is currently suspended and waiting for external stimuli to resume.
    Suspended,
    //
    // 摘要:
    //     The workflow completed successfully.
    Finished,
    //
    // 摘要:
    //     The workflow was cancelled.
    Cancelled,
    //
    // 摘要:
    //     The workflow has faulted.
    Faulted
}

指定流程实例id

在运行时通过RunWorkflowOptions指定WorkflowInstanceId

   RunWorkflowOptions option = null;
   if (!string.IsNullOrEmpty(newInstanceID))
   {
       option = new RunWorkflowOptions
       {
           WorkflowInstanceId = newInstanceID
       };
   }

   return await workflowRunner.RunAsync(workflow, option);

书签示例

   [ActivityAttribute(Category = "Common", Description = nameof(InterruptActivity), DisplayName = nameof(InterruptActivity))]
   public class InterruptActivity : CodeActivity, IActivityNode
   {
       /// <summary>
       /// 
       /// </summary>
       protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
       {
           context.CreateBookmark(new CreateBookmarkArgs()
           {
               Callback = ResumeAsync
           });
           context.CreateBookmark();
       }


       private async ValueTask ResumeAsync(ActivityExecutionContext context)
       {

       }
   }

[参考]

深入Elsa Workflows核心模块:工作流执行引擎