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

推荐订阅源

月光博客
月光博客
Martin Fowler
Martin Fowler
Last Week in AI
Last Week in AI
罗磊的独立博客
阮一峰的网络日志
阮一峰的网络日志
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
V
Visual Studio Blog
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
博客园_首页
人人都是产品经理
人人都是产品经理
量子位
美团技术团队
The Cloudflare Blog
小众软件
小众软件
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
Microsoft Security Blog
Microsoft Security Blog
D
DataBreaches.Net
博客园 - Franky

博客园 - Hey,Coder!

微信小程序 web方案实现调音器功能 systemctl slice配置docker最大占用cpu 内存 pipenv环境变量配置 .net8验证码 数据库差异对比工具 docker部署prometheus grafana pgexporter Alertmanager ubuntu24 换源 ssh配置 ubuntu24 Harbor部署 ubuntu24 kvm部署 cockpit web管理界面 .net api代理 Ubuntu 24.04 DMAR IOMMU 报错修复 uniapp iOS App 上架 vs文件软链接 Docker 网络别名 .net8通用中间件处理公共返回值结构 .net环境下OpenTelemetry Body 读取注意事项 jaeger界面显示异常 .net opentelemetry collector jaeger c# opentelemetry 自定义export c# opentelemetry 自定义export c# 生成对象的初始化代码 Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 Ocelot + Consul + SignalR 粘性会话 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard
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核心模块:工作流执行引擎