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

推荐订阅源

U
Unit 42
博客园 - Franky
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
有赞技术团队
有赞技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
阮一峰的网络日志
阮一峰的网络日志
C
Check Point Blog
爱范儿
爱范儿
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
Stack Overflow Blog
Stack Overflow Blog
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
云风的 BLOG
云风的 BLOG
MyScale Blog
MyScale Blog
Microsoft Security Blog
Microsoft Security Blog
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)

博客园 - 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
基于elsa工作流封装一套变量、组件的体系 常用活动重写
Hey,Coder! · 2026-01-30 · via 博客园 - Hey,Coder!

前言

定义一套支持自定义数据类型的变量管理功能。
通过自定义组件、自定义活动的方式来实现业务组件与工作流组件逻辑分离。
业务组件可以通过不同的版本来保障业务不受更新影响。

备注

如果需要封装类似循环这一类组件时可以使用
context.ScheduleActivities(WhileBody, OnBodyCompleted);
会根据顺序执行,也可以封装成子流程执行

公共定义

public class FlowParseResult
{
    public List<IBaseComponent> ComponentList { get; set; }

    /// <summary>
    /// 数据库需要保存的节点实体列表
    /// </summary>
    public List<FlowNodeEntity> FlowNodeEntityList { get; set; }

    /// <summary>
    /// 数据库需要保存的连接线实体列表
    /// </summary>
    public List<FlowStepEntity> StepEntityList { get; set; }

    /// <summary>
    /// 数据库需要保存的连接线参数列表
    /// </summary>
    public List<FlowParameterEntity> ParameterEntityList { get; set; }

    /// <summary>
    /// 流程变量列表
    /// </summary>
    public List<Variable> FlowVariableList { get; set; }
}

 public enum FlowComponentType
 {
     /// <summary>
     /// 流程开始
     /// </summary>
     [Description("流程开始")]
     StartComp = 1,

     /// <summary>
     /// 流程结束
     /// </summary>
     [Description("流程结束")]
     EndComp = 2,

     /// <summary>
     /// 异常终止
     /// </summary>
     [Description("异常终止")]
     LevelExceptionComp = 3,

     /// <summary>
     /// IF
     /// </summary>
     [Description("IF")]
     IFComp = 4,

     /// <summary>
     /// While
     /// </summary>
     [Description("While")]
     WhileComp = 5,

     /// <summary>
     /// Break
     /// </summary>
     [Description("Break")]
     BreakComp = 6,

     /// <summary>
     /// Assign
     /// </summary>
     [Description("Assign")]
     AssignComp = 7,

     /// <summary>
     /// 无代码平台调用
     /// </summary>
     [Description("NCCaller")]
     NCCallerComp = 8,


     /// <summary>
     /// 延时
     /// </summary>
     [Description("Delay")]
     DelayComp = 9,

     /// <summary>
     /// 通知
     /// </summary>
     [Description("Notify")]
     NotifyComp = 10,
 }

 public class FlowCompSaveDto
 {
     /// <summary>
     /// 画布数据
     /// </summary>
     public string CanvasData { get; set; }
     public List<FlowComponentItem> ComponentItems { get; set; }

     /// <summary>
     /// 节点连线
     /// </summary>
     public List<FlowConnectionDto> Connections { get; set; }

     /// <summary>
     /// 参数列表
     /// </summary>
     public List<FlowParameter> ParameterList { get; set; }
     public string FlowID { get; set; }
 }

public class FlowComponentItem
{
    /// <summary>
    /// 标记是否开始的第一个节点
    /// </summary>
    public bool ISFirst { get; set; }

    /// <summary>
    /// 节点ID
    /// </summary>
    public string NodeID { get; set; }


    /// <summary>
    /// 节点类型
    /// </summary>
    public FlowComponentType NodeType { get; set; }

    /// <summary>
    /// 组件版本等级
    /// </summary>
    public int ComponentVersionLevel { get; set; }

    /// <summary>
    /// 组件配置 - 根据不同组件类型结构不同
    /// </summary>
    public string ConfigValue { get; set; }

    /// <summary>
    /// 节点名称
    /// </summary>
    public string NodeName { get; set; }

    /// <summary>
    /// 描述
    /// </summary>
    public string Description { get; set; }

    public string NodeNo { get;  set; }
}
public class FlowConnectionDto
{
    public string FromID { get; set; }
    public string ToID{ get; set; }
    public string ID { get;  set; }
}
public class FlowParameter
{
    /// <summary>
    /// 数据类型
    /// 支持类型:string、int 、long、double、decimal、bool、datetime
    /// </summary>
    public JsonDataType DataType { get; set; }

    /// <summary>
    /// 数据方向
    /// </summary>
    public DataDirection DataDirection { get; set; }

    /// <summary>
    /// 变量编号
    /// </summary>
    public string ParameterNo { get; set; }

    /// <summary>
    /// 变量名
    /// </summary>
    public string ParameterName { get; set; }

    /// <summary>
    /// 默认值
    /// </summary>
    public string DefaultValue { get; set; }

    /// <summary>
    /// 变量说明
    /// </summary>
    public string Description { get; set; }
    public string ID { get;  set; }
    public string ParentID { get;  set; }

    /// <summary>
    /// 是否必须
    /// </summary>
    public bool IsRequired { get; set; }
}

组件重写

主要通过elsa自带的组件源码作为示例参考重写,然后在这个基础上继续增加扩展的逻辑。
需要注意定义Port,同时Activity需要注册。

IF

[ActivityAttribute(Category = "Common", Description = "IFActivity", DisplayName = "IFActivity")]
public class IFActivity : Activity<bool>, IActivityNode
{

    public ExpressionConfig Condition { get; set; }
    [Port]
    public Activity IFBody { get; set; }
    [Port]
    public Activity ElseBody { get; set; }


    protected override void Execute(ActivityExecutionContext context)
    {
        var conditionValue = Condition.Parse(context);

        Console.WriteLine("if");

        if (conditionValue is not bool boolConditionValue)
        {
            throw new Exception($"ErrIFCondition:{conditionValue}");
        }

        var nextActivity = boolConditionValue ? IFBody : ElseBody;

        context.Set(Result, boolConditionValue);

        if (nextActivity != null)
        {
            context.ScheduleActivityAsync(nextActivity, OnChildCompleted);
        }

    }

    private async ValueTask OnChildCompleted(ActivityCompletedContext context)
    {
        await context.TargetContext.CompleteActivityAsync();
    }
}

break

 [ActivityAttribute(Category = "Common", Description = "BreakActivity", DisplayName = "BreakActivity")]
 public class BreakActivity : CodeActivity, IActivityNode
 {
     protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
     {
         Console.WriteLine("break");
         await context.SendSignalAsync(new BreakSignal());

         await context.CompleteCompositeAsync();
     }
 }

while

[ActivityAttribute(Category = "Common", Description = "While activity", DisplayName = "While")]
public class WhileActivity : Activity, IActivityNode
{
        private bool _shouldBreak = false;

    public ExpressionConfig Condition { get; set; }

    [Port]
    public Activity WhileBody { get; set; }

    /// <summary>
    /// 是否步进模式
    /// </summary>
    public bool ISStepMode { get; set; }

    /// <summary>
    /// 步进模式下,最大值
    /// </summary>
    public ExpressionConfig MaxValue { get; set; }

    /// <summary>
    /// 步进模式下,最小值
    /// </summary>
    public ExpressionConfig MinValue { get; set; }

    /// <summary>
    /// 步进模式下,步长
    /// </summary>
    public ExpressionConfig StepValue { get; set; }

    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
    {
        Console.WriteLine("while");
        await HandleIterationAsync(context);
    }

    protected override void OnSignalReceived(object signal, SignalContext context)
    {
        if (signal is BreakSignal)
        {
            _shouldBreak = true;
        }
        base.OnSignalReceived(signal, context);
    }

    private async ValueTask OnBodyCompleted(ActivityCompletedContext context)
    {
        await HandleIterationAsync(context.TargetContext);
    }

    private async ValueTask HandleIterationAsync(ActivityExecutionContext context)
    {

        //var isBreaking = context.GetIsBreaking();
if (_shouldBreak)
{
    await context.CompleteActivityAsync();
    return;
}

var conditionValue = Condition.Parse(context);
//var boolConditionValue = true;

if (conditionValue is not bool boolConditionValue)
{
    throw new Exception($"ErrIFCondition:{conditionValue}");
}

//如果没配置子节点时直接退出,避免可能的死循环
if (!boolConditionValue || WhileBody == null)
{
    await context.CompleteActivityAsync();
    return;
}

//context.ScheduleActivityAsync(WhileBody, OnBodyCompleted);
await context.ScheduleActivityAsync(WhileBody, OnBodyCompleted);
    }
}

nuget

使用DynamicExpresso.Core.2.19.3
Elsa 3.5.3
Elsa.EntityFrameworkCore 3.5.3
Elsa.EntityFrameworkCore.Postgresql 3.5.3

基础类型定义

定义数据结构支持的字段类型

/// <summary>
/// Json数据类型
/// </summary>
public enum JsonDataType
{
    /// <summary>
    /// 未设定
    /// </summary>
    None = 0,
    /// <summary>
    /// 字符串
    /// </summary>
    String,
    /// <summary>
    /// 整形
    /// </summary>
    Integer,
    /// <summary>
    /// 长整形
    /// </summary>
    Long,
    /// <summary>
    /// Double类型
    /// </summary>
    Double,
    /// <summary>
    /// Decimal类型
    /// </summary>
    Decimal,
    /// <summary>
    /// 布尔
    /// </summary>
    Boolean,
    /// <summary>
    /// 日期时间
    /// </summary>
    DateTime,
    /// <summary>
    /// UUID
    /// </summary>
    Guid,
    /// <summary>
    /// 对象
    /// </summary>
    Object,
    /// <summary>
    /// 数组
    /// </summary>
    Array,
    /// <summary>
    /// Null类型
    /// </summary>
    Null,

    Dynamic = 12,
    /// <summary>
    /// 分页列表
    /// </summary>
    PagedList = 13,

    /// <summary>
    /// List<string>
    /// </summary>
    ListStr = 14,

    /// <summary>
    ///  List<int>
    /// </summary>
    ListInt = 15
}

变量类型定义

定义底层处理时支持的变量的类型,其中链式表达式可以支持配置复杂的条件与或非等逻辑

public enum AssignTypeEnum
{
    /// <summary>
    /// 引用
    /// </summary>
    [Description("引用变量")]
    Ref = 0,

    /// <summary>
    /// 常量(手写)
    /// </summary>
    [Description("常量")]
    ConstValue = 1,

    [Description("系统变量")]
    SystemValue = 2,


    /// <summary>
    /// 链式表达式
    /// </summary>
    [Description("表达式")]
    LinkExpression = 3,
}

参数解析帮助类

辅助转换入参为具体类型

public class FlowParameterUtil
{
    public static List<Variable> ParameterParse(List<FlowParameterEntity> parameters)
    {
        if (parameters == null || parameters.Count == 0)
        {
            return new();
        }

        var result = new List<Variable>();
        foreach (var parameter in parameters)
        {
            object paraItem = ParseDefaultValue(parameter.ParameterType, parameter.DefaultValue);

            var variable = ParseFlowParameter(paraItem, parameter.ParameterNo);
            result.Add(variable);
        }

        return result;
    }

    public static Variable ParameterParse(JsonDataType dataType, string parameterNo, string value)
    {
        //TODO 补充异常入参校验

        object paraItem = ParseDefaultValue(dataType, value);

        var variable = ParseFlowParameter(paraItem, parameterNo);

        return variable;
    }

    public static Input<T> ParseFlowInput<T>(object obj, string paraName)
    {
        var para = new Input<T>((T)obj, paraName);
        return para;
    }

    public static Variable<T> ParseFlowParameter<T>(object obj, string paraName)
    {
        var variable = new Variable<T>(paraName, (T)obj);
        return variable;
    }

    public static Variable ParseFlowParameter(object obj, string paraName)
    {
        var variable = new Variable(paraName, obj);
        return variable;
    }

    /// <summary>
    /// support type for string、int 、long、double、decimal、bool、datetime
    /// </summary>
    /// <param name="jsonDataType"></param>
    /// <param name="defaultValue"></param>
    /// <returns></returns>
    /// <exception cref="System.Exception"></exception>
    /// <exception cref="System.FormatException"></exception>
    public static object ParseDefaultValue(JsonDataType jsonDataType, string defaultValue)
    {
        if (string.IsNullOrEmpty(defaultValue))
        {
            return GetDefaultValueForType(jsonDataType);
        }

        switch (jsonDataType)
        {
            case JsonDataType.String:
                return defaultValue;
            case JsonDataType.Integer:
                return int.TryParse(defaultValue, out var intResult) ? intResult : throw new System.FormatException($"ErrInt:'{defaultValue}'");
            case JsonDataType.Long:
                return long.TryParse(defaultValue, out var longResult) ? longResult : throw new System.FormatException($"ErrLong:'{defaultValue}'");
            case JsonDataType.Double:
                return double.TryParse(defaultValue, out var doubleResult) ? doubleResult : throw new System.FormatException($"ErrDouble:'{defaultValue}'");
            case JsonDataType.Decimal:
                return decimal.TryParse(defaultValue, out var decimalResult) ? decimalResult : throw new System.FormatException($"ErrDecimal:'{defaultValue}'");
            case JsonDataType.Boolean:
                return bool.TryParse(defaultValue, out var boolResult) ? boolResult : throw new System.FormatException($"ErrBoolean:'{defaultValue}'");
            case JsonDataType.DateTime:
                return System.DateTime.TryParse(defaultValue, out var dateTimeResult) ? dateTimeResult : throw new System.FormatException($"ErrDateTime:'{defaultValue}'");
            case JsonDataType.Guid:
                return System.Guid.TryParse(defaultValue, out var guidResult) ? guidResult : throw new System.FormatException($"ErrGuid:'{defaultValue}'");
            case JsonDataType.Object:
            case JsonDataType.Array:
            case JsonDataType.Null:
            case JsonDataType.Dynamic:
            case JsonDataType.PagedList:
            case JsonDataType.ListStr:
            case JsonDataType.ListInt:
            case JsonDataType.None:
            default:
                throw new System.Exception($"UnsupportDataType: {jsonDataType}");
        }
    }

    private static object GetDefaultValueForType(JsonDataType jsonDataType)
    {
        switch (jsonDataType)
        {
            case JsonDataType.None:
                return null;
            case JsonDataType.String:
                return string.Empty;
            case JsonDataType.Integer:
                return 0;
            case JsonDataType.Long:
                return 0L;
            case JsonDataType.Double:
                return 0.0;
            case JsonDataType.Decimal:
                return 0.0m;
            case JsonDataType.Boolean:
                return false;
            case JsonDataType.DateTime:
                return System.DateTime.MinValue;
            case JsonDataType.Guid:
                return System.Guid.Empty;
            case JsonDataType.Object:
                return null;
            case JsonDataType.Array:
                return null;
            case JsonDataType.Null:
                return null;
            case JsonDataType.Dynamic:
                return null;
            case JsonDataType.PagedList:
                return null;
            case JsonDataType.ListStr:
                return null;
            case JsonDataType.ListInt:
                return null;
            default:
                return null;
        }
    }
}

系统变量类型

 public enum SysValueTypeEnum
 {
     GUID = 0,
     SystemTime = 1,
     LoginUser = 2,
     LoginUserNo = 3,
     EmptyStr = 4,
     NULL = 5
 }

系统变量解析

public class ExpSysValue
{
    public SysValueTypeEnum ValueType { get; set; }

    public object Parse()
    {
        switch (ValueType)
        {
            case SysValueTypeEnum.GUID:
                return ObjectId.NewId();
            case SysValueTypeEnum.SystemTime:
                return DateTime.Now;
            case SysValueTypeEnum.LoginUser:
                return "Passport.UserId";
            case SysValueTypeEnum.LoginUserNo:
                return "Passport.UserId";
            case SysValueTypeEnum.EmptyStr:
                return string.Empty;
            case SysValueTypeEnum.NULL:
                return null;
            default:
                throw new NotImplementedException("ErrSysValueType");
        }
    }
}

常量解析

public class ExpConst
{
    public string Value { get; set; }

    /// <summary>
    /// 常量时使用
    /// </summary>
    public JsonDataType ValueDataType { get; set; }

    public object Parse(ActivityExecutionContext context)
    {
        var variable = FlowParameterUtil.ParseDefaultValue(ValueDataType, Value);
        return variable;
    }
}

变量解析

public class ExpRef
{
    /// <summary>
    /// 引用变量的编号
    /// </summary>
    public string RefParamateNo { get; set; }


    public object Parse(ActivityExecutionContext context)
    {
        var result = context.GetVariable<object>(RefParamateNo);
        return result;
    }
}

动态表达式

public enum ExpLinkTypeEnum
{
    /// <summary>
    /// 除法运算 (a / b)
    /// </summary>
    Divide = 1,

    /// <summary>
    /// 等于比较 (a == b)
    /// </summary>
    Equal = 2,

    /// <summary>
    /// 按位或逻辑异或运算 (a ^ b)
    /// </summary>
    ExclusiveOr = 3,

    /// <summary>
    /// 大于比较 (a > b)
    /// </summary>
    GreaterThan = 4,

    /// <summary>
    /// 大于等于比较 (a >= b)
    /// </summary>
    GreaterThanOrEqual = 5,

    /// <summary>
    /// 小于比较 (a < b)
    /// </summary>
    LessThan = 6,

    /// <summary>
    /// 小于等于比较 (a <= b)
    /// </summary>
    LessThanOrEqual = 7,

    /// <summary>
    /// 算术取余运算 (a % b)
    /// </summary>
    Modulo = 8,

    /// <summary>
    /// 乘法运算 (a * b)
    /// </summary>
    Multiply = 9,

    /// <summary>
    /// 算术取反运算 (-a)
    /// </summary>
    Negate = 10,

    /// <summary>
    /// 按位取反或逻辑非运算 (~a 或 !a)
    /// </summary>
    Not = 11,

    /// <summary>
    /// 不等于比较 (a != b)
    /// </summary>
    NotEqual = 12,

    /// <summary>
    /// 按位或逻辑或运算 (a | b)
    /// </summary>
    Or = 13,

    /// <summary>
    /// 短路条件或运算 (a || b)
    /// </summary>
    OrElse = 14,

    /// <summary>
    /// 减法运算 (a - b)
    /// </summary>
    Subtract = 15,

    /// <summary>
    /// 一元递减运算 (a - 1)
    /// </summary>
    Decrement = 16,

    /// <summary>
    /// 一元递增运算 (a + 1)
    /// </summary>
    Increment = 17,
}
 public class ExpLink
 {
     public ExpressionConfig LeftExp { get; set; }

     public ExpressionConfig RightExp { get; set; }

     public ExpLinkTypeEnum LinkType { get; set; }

     public object Parse(ActivityExecutionContext context)
     {
         //null值校验
         ExceptionHelper.CheckNull(LeftExp, "LeftExpNotNull");
         //允许右值为空
         //ExceptionHelper.CheckNull(RightExp, "RightExpNotNull");

         var leftValue = LeftExp.Parse(context);
         object rightValue = null;
         if (RightExp != null)
         {
             rightValue = RightExp.Parse(context);
         }

         var result = Parse(leftValue, rightValue, LinkType);
         return result;
     }

     private object Parse(object leftValue, object rightValue, ExpLinkTypeEnum linkType)
     {
         var interpreter = new Interpreter();

         switch (linkType)
         {
             case ExpLinkTypeEnum.Divide:
                 return interpreter.Eval("leftValue/rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Equal:
                 return leftValue.Equals(rightValue);
             case ExpLinkTypeEnum.ExclusiveOr:
                 return interpreter.Eval("leftValue^rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.GreaterThan:
                 return interpreter.Eval("leftValue>rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.GreaterThanOrEqual:
                 return interpreter.Eval("leftValue>=rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.LessThan:
                 return interpreter.Eval("leftValue<rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.LessThanOrEqual:
                 return interpreter.Eval("leftValue<=rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Modulo:
                 return interpreter.Eval("leftValue%rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Multiply:
                 return interpreter.Eval("leftValue*rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Negate:
                 return interpreter.Eval("-leftValue",
                     new Parameter("leftValue", leftValue)
                     );
             case ExpLinkTypeEnum.Not:
                 return interpreter.Eval("!leftValue",
                     new Parameter("leftValue", leftValue)
                     );
             case ExpLinkTypeEnum.NotEqual:
                 return !leftValue.Equals(rightValue);
             case ExpLinkTypeEnum.Or:
                 return interpreter.Eval("leftValue | rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.OrElse:

                 return interpreter.Eval("leftValue || rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Subtract:
                 return interpreter.Eval("leftValue - rightValue",
                     new Parameter("leftValue", leftValue),
                     new Parameter("rightValue", rightValue)
                     );
             case ExpLinkTypeEnum.Decrement:
                 return interpreter.Eval("leftValue - 1",
                     new Parameter("leftValue", leftValue)
                     );
             case ExpLinkTypeEnum.Increment:
                 return interpreter.Eval("leftValue + 1",
                     new Parameter("leftValue", leftValue)
                     );
             default:
                 break;
         }

         return leftValue;
     }
 }

变量解析

/// <summary>
/// 单个表达式节点解析
/// </summary>
public class ExpressionConfig
{
    public AssignTypeEnum ExpressionNodeType { get; set; }

    public string ConfigValue { get; set; }

    public object Parse(ActivityExecutionContext context)
    {
        if (string.IsNullOrEmpty(ConfigValue))
        {
            throw new System.Exception("ExpConfigNotNUll");
        }
        switch (ExpressionNodeType)
        {
            case AssignTypeEnum.Ref:
                var refData = JsonConvert.DeserializeObject<ExpRef>(ConfigValue);
                return refData.Parse(context);
            case AssignTypeEnum.ConstValue:
                var constValue = JsonConvert.DeserializeObject<ExpConst>(ConfigValue);
                return constValue.Parse(context);
            case AssignTypeEnum.SystemValue:
                var systemValueConfig = JsonConvert.DeserializeObject<ExpSysValue>(ConfigValue);
                return systemValueConfig.Parse();
            case AssignTypeEnum.LinkExpression:
                var link = JsonConvert.DeserializeObject<ExpLink>(ConfigValue);
                return link.Parse(context);
            default:
                throw new System.Exception("errExpression");
        }
    }


        /// <summary>
        /// 获取表达式对应的数值
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="System.Exception"></exception>
        public object Parse(ActivityExecutionContext context)
        {
            if (string.IsNullOrEmpty(ConfigValue))
            {
                throw new System.Exception("ExpConfigNotNUll");
            }

            IExpBase exp = ParseExpInfo();
            return exp.Parse(context);
        }

        public T Parse<T>(ActivityExecutionContext context)
        {
            var result = Parse(context);

            if(result is not T)
            {
                throw new System.Exception("ErrType");
            }

            return (T)result;
        }

        public static Dictionary<string, object> Parse(ActivityExecutionContext context, Dictionary<string, ExpressionConfig> para)
        {
            var paraResult = new Dictionary<string, object>();
            if (para.IsNullOrEmpty())
            {
                return paraResult;
            }

            foreach (var paraItem in para)
            {
                var paraValueItem = paraItem.Value.Parse(context);
                paraResult.Add(paraItem.Key, paraValueItem);
            }

            return paraResult;
        }
}

组件定义

public abstract class IBaseComponent
{
    public string NodeID { get; set; }
    public string NodeNo { get; set; }
    public string NodeName { get; set; }

    /// <summary>
    /// 节点类型
    /// </summary>
    public FlowComponentType NodeType { get; set; }

    /// <summary>
    /// 节点版本
    /// </summary>
    public int NodeTypeVersion { get; set; }

    [JsonIgnore]
    public Activity FlowNode { get; set; }


    /// <summary>
    /// 解析组件基础结构
    /// </summary>
    /// <returns></returns>
    public abstract IBaseComponent Parse(FlowParseContext parseContext);

    /// <summary>
    /// 解析组件额外属性
    /// 所有组件基础结构完成后再次解析配置
    /// 用于识别嵌套、递归的子组件等逻辑
    /// </summary>
    /// <returns></returns>
    public abstract void ParseExtra(FlowParseContext parseContext);


    /// <summary>
    /// 
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public static IBaseComponent ParseAPIComponent(FlowComponentItem item)
    {
        ExceptionHelper.Check(string.IsNullOrEmpty(item.ConfigValue), $"EmptyNodeConfig:{item.NodeName}");

        Type componentType = null;

        try
        {
            componentType = ICompFactory.GetAPIComponent(item.NodeType, item.ComponentVersionLevel);

        }
        catch (Exception)
        {
            throw new Exception($"NodeTypeParseErr:{item.NodeName}");
        }
        IBaseComponent component = null;

        try
        {
            component = JsonConvert.DeserializeObject(item.ConfigValue, componentType) as IBaseComponent;
        }
        catch (Exception)
        {
            throw new Exception($"NodeParseErr:{item.NodeName}");
        }

        component.NodeID = item.NodeID;
        component.NodeNo = item.NodeNo;
        component.NodeName = item.NodeName;
        component.NodeType = item.NodeType;
        component.NodeTypeVersion = item.ComponentVersionLevel;

        return component;
    }

    protected void InitActivity()
    {
        if (FlowNode is not Activity activity)
        {
            throw new Exception("FlowNode is not ActivityNode");
        }

        activity.Id = NodeID;
        activity.NodeId = NodeID;
        activity.Name = NodeName;
    }
}

组件工厂

public interface ICompFactory
{
    public abstract Type CreateComponent(int compLevel);

    public static Type GetAPIComponent(FlowComponentType nodeType, int compLevel)
    {
        var compFactory = CompFactoryContainer.GetOrAddFactory(nodeType);
        var componentType = compFactory.CreateComponent(compLevel);
        return componentType;
    }
}

internal static class CompFactoryContainer
{
    private static readonly ConcurrentDictionary<FlowComponentType, ICompFactory> _factoryCache = new();

    static CompFactoryContainer()
    {
        // 预初始化所有工厂实例
        _factoryCache.TryAdd(FlowComponentType.StartComp, new StartCompFactory());
        _factoryCache.TryAdd(FlowComponentType.EndComp, new EndCompFactory());
        _factoryCache.TryAdd(FlowComponentType.LevelExceptionComp, new LevelExceptionCompFactory());
        _factoryCache.TryAdd(FlowComponentType.IFComp, new IFCompFactory());
    }

    public static ICompFactory GetOrAddFactory(FlowComponentType nodeType)
    {
        if (_factoryCache.TryGetValue(nodeType, out var factory))
        {
            return factory;
        }

        throw new NotImplementedException($"ComponentFactoryNotFound: {nodeType}");
    }
}

解析上下文

public class FlowParseContext
{
    /// <summary>
    /// 组件解析结果
    /// </summary>
    public FlowParseResult ParseResult { get; set; }

    /// <summary>
    /// 查找节点
    /// </summary>
    /// <param name="nodeNo"></param>
    public IBaseComponent FindNode(string nodeNo)
    {
        ExceptionHelper.CheckNull(ParseResult, "ParseResultNull");
        var result = ParseResult.ComponentList?.FirstOrDefault(f => f.NodeNo == nodeNo);
        ExceptionHelper.CheckNull(result, $"NodeNotFound:{nodeNo}");
        return result;
    }

    /// <summary>
    /// 查找变量
    /// </summary>
    /// <param name="variableNo"></param>
    public Variable FindVariable(string variableNo)
    {
        ExceptionHelper.CheckNull(ParseResult, "ParseResultNull");
        var result = ParseResult.FlowVariableList?.FirstOrDefault(f => f.Name == variableNo);
        ExceptionHelper.CheckNull(result, $"VariableNotFound:{variableNo}");
        return result;
    }



    public List<IBaseComponent> FindNodeList(List<string> nodeNoList)
    {
        if (ParseResult == null || ParseResult.ComponentList == null || nodeNoList == null || nodeNoList.Count == 0)
        {
            return null;
        }
        ExceptionHelper.CheckNull(ParseResult, "ParseResultNull");
        var result = ParseResult.ComponentList.Where(f => nodeNoList.Contains(f.NodeNo)).ToList();
        ExceptionHelper.CheckNull(result, $"NodeNotFound:{nodeNoList}");
        return result;
    }

    /// <summary>
    /// 解析子流程
    /// </summary>
    /// <param name="subFlow"></param>
    /// <returns></returns>
    public Flowchart ParseSubFlow(FlowSubChartDto subFlow)
    {
        if (subFlow == null || subFlow.ComponentNoList == null || subFlow.ComponentNoList.Count == 0)
        {
            return null;
        }

        var nextNode = FindNodeList(subFlow.ComponentNoList);

        var subAct = new Flowchart
        {
            Activities = new List<Elsa.Workflows.IActivity>()
        };

        var flowNodeList = nextNode.Select(f => f.FlowNode).ToList();
        subAct.Activities.AddRange(flowNodeList);

        //处理节点连线
        subAct.Connections = new List<Connection>();

        foreach (var item in subFlow.Connections)
        {
            var fromAct = FindNode(item.FromID);
            var toAct = FindNode(item.ToID);
            ExceptionHelper.CheckNull(fromAct, $"NodeNull,NodeID:{item.FromID}");
            ExceptionHelper.CheckNull(toAct, $"NodeNull,NodeID:{item.ToID}");

            if (fromAct.FlowNode is not Activity fromActivity)
            {
                throw new Exception("ErrActivity");
            }

            if (toAct.FlowNode is not Activity toActivity)
            {
                throw new Exception("ErrActivity");
            }
            var connectionItem = new Connection(fromActivity, toActivity);
            subAct.Connections.Add(connectionItem);
        }

        return subAct;
    }
}

示例组件 IF组件

  public class IFCompFactory : ICompFactory
  {
      public Type CreateComponent(int compLevel)
      {
          switch (compLevel)
          {
              case 1:
                  return typeof(IFComponent);
              default:
                  throw new ArgumentException("UnsupportComponentVersion");
          }
      }
  }


public class IFComponent : IBaseComponent
{
    /// <summary>
    /// 条件
    /// </summary>
    public ExpressionConfig Condition { get; set; }

    public string IFNodeNo { get; set; }

    public string ElseNodeNo { get; set; }


    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override IBaseComponent Parse(FlowParseContext parseContext)
    {
        //TODO 解析节点和流程配置
        FlowNode = new IFActivity();

        InitActivity();

        return this;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override void ParseExtra(FlowParseContext parseContext)
    {
        ExceptionHelper.CheckNull(Condition, $"ConditionNull:{NodeName}");

        if (FlowNode is not IFActivity ifNode)
        {
            throw new System.Exception($"ErrIFNode:{NodeName}");
        }

        ifNode.Condition = Condition;

        //基础解析完成后,解析子节点
        if (IFNodeNo != null)
        {
            var ifBodeNode = parseContext.FindNode(IFNodeNo);
            ifNode.IFBody = ifBodeNode.FlowNode;
        }

        if (ElseNodeNo != null)
        {
            var elseNode = parseContext.FindNode(ElseNodeNo);
            ifNode.ElseBody = elseNode.FlowNode;
        }
    }
}

public class IFActivity : CodeActivity<bool>, IActivityNode
{

    public ExpressionConfig Condition { get; set; }
    public Activity IFBody { get; set; }
    public Activity ElseBody { get; set; }

    protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
    {
        var conditionValue = Condition.Parse(context);

        Console.WriteLine("if");

        if (conditionValue is not bool boolConditionValue)
        {
            throw new Exception($"ErrIFCondition:{conditionValue}");
        }

        var nextActivity = boolConditionValue ? IFBody : ElseBody;

        context.Set(Result, boolConditionValue);

        if (nextActivity != null)
        {
            context.ScheduleActivityAsync(nextActivity, OnChildCompleted);
        }

        return base.ExecuteAsync(context);
    }

    private async ValueTask OnChildCompleted(ActivityCompletedContext context)
    {
        await context.TargetContext.CompleteActivityAsync();
    }
}

组件执行

/// <summary>
/// 流程执行器
/// </summary>
public class FlowExecuter
{
    /// <summary>
    /// 执行流程
    /// 可以在保存时直接执行,避免多次调用数据库
    /// </summary>
    public static async Task<RunWorkflowResult> ExecFlow(IWorkflowRunner workflowRunner, FlowParseResult parseResult, Dictionary<string, object> para)
    {
        var workflow = ParseWorkFlow(parseResult, para);

        return await workflowRunner.RunAsync(workflow);
    }

    /// <summary>
    /// 恢复流程
    /// 可以在保存时直接执行,避免多次调用数据库
    /// </summary>
    public static async Task<RunWorkflowResult> ResumeFlow(IWorkflowRunner workflowRunner, IWorkflowInstanceStore _workflowInstanceStore, FlowParseResult parseResult, Dictionary<string, object> para, string instanceID)
    {
        var workflow = ParseWorkFlow(parseResult, para);

        if (string.IsNullOrEmpty(instanceID))
        {
            throw new Exception("InstanceNull");
        }
        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(workflow, workflowInstance.WorkflowState, options);
        return await workflowRunner.RunAsync(workflow);
    }


    private static Workflow ParseWorkFlow(FlowParseResult parseResult, Dictionary<string, object> para)
    {
        List<Variable> variables = null;
        var parseContext = new FlowParseContext
        {
            ParseResult = parseResult,
        };

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

        //解析基础结构
        foreach (var item in parseResult.ComponentList)
        {
            item.Parse(parseContext);
        }

        if (parseResult.FlowVariableList.IsObjNotNull())
        {
            variables = [.. parseResult.FlowVariableList];

            //入参处理
            var inputParaf = FlowParameterUtil.ParameterInParse(parseResult.ParameterEntityList, para);
            variables = FlowParameterUtil.ParameterInReplace(parseResult.FlowVariableList, inputParaf);
        }

        //解析扩展数据 需要等所有的组件和变量完成后进行处理
        foreach (var item in parseResult.ComponentList)
        {
            item.ParseExtra(parseContext);
        }

        //生成执行节点
        foreach (var item in parseResult.ComponentList)
        {
            if (item.FlowNode is not Activity activity)
            {
                throw new Exception("ErrActivity");
            }

            workflow.Activities.Add(activity);
        }

        //节点连线处理
        if (parseResult.StepEntityList.IsObjNotNull())
        {
            workflow.Connections = new List<Connection>();

            foreach (var item in parseResult.StepEntityList)
            {
                var fromAct = workflow.Activities.FirstOrDefault(f => f.Id == item.FromId);
                var toAct = workflow.Activities.FirstOrDefault(f => f.Id == item.ToId);
                ExceptionHelper.CheckNull(fromAct, $"NodeNull,NodeID:{item.FromId}");
                ExceptionHelper.CheckNull(toAct, $"NodeNull,NodeID:{item.ToId}");

                var connectionItem = new Connection(fromAct, toAct);
                workflow.Connections.Add(connectionItem);
            }
        }

        var result = new Workflow
        {
            Root = workflow,
            Variables = variables
        };
        return result;
    }

    #region 流程数据解析

    /// <summary>
    /// 解析前端配置
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    public static List<IBaseComponent> ParseNode(FlowCompSaveDto dto)
    {
        ExceptionHelper.CheckNullOrEmpty(dto.ComponentItems, "NodeNull");

        List<IBaseComponent> parseResult = new();
        foreach (var item in dto.ComponentItems)
        {
            var parseItem = IBaseComponent.ParseAPIComponent(item);
            ExceptionHelper.CheckNull(parseItem, $"ParseResultNull,NodeName:{item.NodeName}");

            parseResult.Add(parseItem);
        }

        return parseResult;
    }

    /// <summary>
    /// 根据入参和解析结果,生成需要保存的节点数据
    /// </summary>
    /// <param name="dto"></param>
    /// <param name="parseResult"></param>
    /// <returns></returns>
    public static List<FlowNodeEntity> ConvertNodeEntity(FlowCompSaveDto dto, List<IBaseComponent> parseResult)
    {
        List<FlowNodeEntity> nodeList = new();

        foreach (var item in parseResult)
        {
            ExceptionHelper.Check(item == null, "NodeParseNull");

            var nodeItem = new FlowNodeEntity();
            nodeItem.Id = item.NodeID;
            nodeItem.FlowId = dto.FlowID;

            var rawConfig = dto.ComponentItems.FirstOrDefault(x => x.NodeID == item.NodeID);
            ExceptionHelper.Check(rawConfig == null, "NodeConfigNull");

            nodeItem.NodeName = rawConfig.NodeName;
            nodeItem.NodeNo = rawConfig.NodeNo;
            nodeItem.NodeType = rawConfig.NodeType;
            nodeItem.IsStartNode = rawConfig.ISFirst;

            nodeList.Add(nodeItem);
        }

        return nodeList;
    }

    public static List<FlowStepEntity> ParseConnections(FlowCompSaveDto dto)
    {
        if (dto == null || dto.Connections.IsObjNull())
        {
            return null;
        }

        var result = new List<FlowStepEntity>();
        foreach (var item in dto.Connections)
        {
            var step = new FlowStepEntity
            {
                Id = item.ID,
                FlowId = dto.FlowID,
                FromId = item.FromID,
                ToId = item.ToID,
            };

            result.Add(step);
        }
        return result;
    }

    public static List<FlowParameterEntity> ParseParameter(FlowCompSaveDto dto)
    {
        if (dto.IsObjNull() || dto.ParameterList.IsObjNull())
        {
            return null;
        }

        var result = new List<FlowParameterEntity>();
        foreach (var item in dto.ParameterList)
        {
            var parameter = new FlowParameterEntity
            {
                Id = item.ID,
                FlowId = dto.FlowID,
                ParameterNo = item.ParameterNo,
                ParameterName = item.ParameterName,
                ParameterType = item.DataType,
                DefaultValue = item.DefaultValue,
                Description = item.Description,
                ParentId = item.ParentID,
                IsRequired = item.IsRequired,
                DataDirection = item.DataDirection
            };
            result.Add(parameter);

        }
        return result;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static FlowParseResult ParseConfig(FlowCompSaveDto dto)
    {
        var parseResult = FlowExecuter.ParseNode(dto);
        var nodeList = FlowExecuter.ConvertNodeEntity(dto, parseResult);
        var stepList = FlowExecuter.ParseConnections(dto);
        var paraList = FlowExecuter.ParseParameter(dto);

        var result = new FlowParseResult
        {
            ComponentList = parseResult,
            FlowNodeEntityList = nodeList,
            StepEntityList = stepList,
            ParameterEntityList = paraList
        };

        if (paraList.IsObjNotNull())
        {
            //解析变量
            var variable = FlowParameterUtil.ParameterParse(paraList);
            result.FlowVariableList = [.. variable];
        }

        return result;
    }
    #endregion
}

帮助类

public static class ExceptionHelper
{
    public static void Check(bool checkValue, string msg)
    {
        if (!checkValue)
        {
            return;
        }
        throw new System.Exception(msg);
    }

    public static void CheckNull(object obj, string msg)
    {
        if (obj != null)
        {
            return;
        }
        throw new System.Exception(msg);
    }

    public static void CheckNullOrEmpty(IList obj, string msg)
    {
        if (obj != null && obj.Count > 0)
        {
            return;
        }
        throw new System.Exception(msg);
    }
}

其他重要内容

  • 取消工作流
    context.WorkflowExecutionContext.Cancel();

示例如下

 [ActivityAttribute(Category = "Common", Description = "EndActivity", DisplayName = "EndActivity")]
 public class EndActivity : CodeActivity, IActivityNode
 {
     protected override void Execute(ActivityExecutionContext context)
     {
         Console.WriteLine("end");

         //context.CompleteCompositeAsync();

         //取消当前工作流执行
         context.WorkflowExecutionContext.Cancel();


         //TODO 处理返回值
     }
 }

如果只需要停止当前的子流程可以使用context.CompleteCompositeAsync();

如果组件之间需要通信,可以使用single机制,类似while中break,具体内容可以查看上文代码中的示例