1.WorkflowManagement — 工作流生命周期管理

一、这个模块是做什么的?

Elsa.Workflows.Management 是工作流的"管理层",专门负责工作流的"静态"生命周期,即从一个工作流被创建保存为草稿发布上线版本回退删除下线,整个过程的流转都由这个模块统一管理。

它不负责"运行"工作流(那是 Elsa.Workflows.Runtime 干的事),它只管工作流中有哪些元素以及这个工作流能不能被执行

二、工作流定义的核心概念:版本与状态

在深入代码之前,先理解两个贯穿始终的核心概念。

DefinitionId vs Id

  • 每个工作流有两个 ID。DefinitionId 是工作流的"永久身份证",是一个 string 类型,整个工作流无论有多少个版本,DefinitionId 始终不变。而 Id 是每个具体版本记录的主键(同样是 string,通常是一个 ShortGuid),每产生一个新版本就是一个新的 Id
  • 可以把 DefinitionId 理解为"这本书的书名",Id 理解为"这本书第3版的具体编号"。

IsLatest 与 IsPublished 两个标志位

  • 每条工作流定义记录上有两个布尔标志,IsLatest 表示"这是当前最新的版本",IsPublished 表示"这个版本已经发布、可以被实际执行"。一个工作流同一时间只能有一条记录的 IsPublished = true,也只能有一条记录的 IsLatest = true版本管理的核心就是维护好这两个标志位

三、定义的创建:从零开始建一个工作流

  • 一个全新的工作流定义,起点在 WorkflowDefinitionPublisher.New() 方法:

    // Services/WorkflowDefinitionPublisher.cs 第27行
    public WorkflowDefinition New(IActivity? root = null)
    {
        root ??= new Sequence();
        var id = identityGenerator.GenerateId();           // 生成 string 类型的 Id
        var definitionId = identityGenerator.GenerateId(); // 生成 string 类型的 DefinitionId
        const int version = 1;
    
        return new()
        {
            Id = id,
            DefinitionId = definitionId,
            Version = version,
            IsLatest = true,
            IsPublished = false,
            CreatedAt = systemClock.UtcNow,
            StringData = activitySerializer.Serialize(root),
            MaterializerName = JsonWorkflowMaterializer.MaterializerName
        };
    }
    
    • 当调用该创建方法时,会先生成两个 string 类型的 ID(默认实现是 ShortGuidIdentityGenerator,生成紧凑的 Base64 格式 GUID 字符串)。
    • 根节点默认是一个空的 Sequence,通过 IActivitySerializer 序列化为 JSON 字符串存入 StringData 字段。
    • MaterializerName 标记为 Json,说明将来用 JsonWorkflowMaterializer 来还原这个工作流。

    这个方法只是在内存中构建了对象,还没写入数据库。真正的持久化要靠下一步——保存草稿。

四、保存草稿

  • 草稿的保存入口是 WorkflowDefinitionPublisher.SaveDraftAsync() 方法:

    // Services/WorkflowDefinitionPublisher.cs 第216行
    var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(filter, cancellationToken);
    
    draft.Version = draft.Id == lastVersion?.Id ? lastVersion.Version : lastVersion?.Version + 1 ?? 1;
    draft.IsLatest = true;
    draft = Initialize(draft);
    
  • 版本号计算:先通过 FindLastVersionAsync() 查出当前 DefinitionId 下版本号最大的那条记录。然后判断:如果传入的 draft.Id 和这条记录的 Id 相同,说明是在编辑同一个草稿,版本号不变;否则版本号 +1;如果数据库里完全没有记录,则从 1 开始。

  • 推出旧的"最新"标记:如果最后一个版本同时是 IsPublished = trueIsLatest = true,需要把它的 IsLatest 置为 false,腾位置给新草稿(第229行):

    // Services/WorkflowDefinitionPublisher.cs 第229行
    if (lastVersion is { IsPublished: true, IsLatest: true })
    {
        lastVersion.IsLatest = false;
        await workflowDefinitionStore.SaveAsync(lastVersion, cancellationToken);
    }```
    
    
  • 通知机制

    // Services/WorkflowDefinitionPublisher.cs 第222行
    await mediator.SendAsync(new WorkflowDefinitionDraftSaving(draft), cancellationToken);
    await workflowDefinitionStore.SaveAsync(draft, cancellationToken);
    await mediator.SendAsync(new WorkflowDefinitionDraftSaved(draft), cancellationToken);
    
    • Elsa 使用 IMediator.SendAsync() 发出通知(内部由 INotificationSender 实现,采用 MediatR 风格的 Handler 分发)。
    • WorkflowDefinitionDraftSaving 的消费者是 EvictWorkflowDefinitionServiceCache,它立即调用 WorkflowDefinitionCacheManager.EvictWorkflowDefinitionAsync() 让缓存失效。这里的缓存就是框架维护的所有当前可用的工作流。
    • 如果是第一次保存(lastVersion == null),还会发出 WorkflowDefinitionCreated 通知:
      // Services/WorkflowDefinitionPublisher.cs 第226行
      if (lastVersion is null)
          await mediator.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken);
      

五、发布

  • 发布是整个生命周期中最关键的操作,入口是 WorkflowDefinitionPublisher.PublishAsync() 方法(Services/WorkflowDefinitionPublisher.cs 第85行)。

  • 第一步:物化并验证工作流对象

    // Services/WorkflowDefinitionPublisher.cs 第87行
    var workflowGraph = await workflowDefinitionService.MaterializeWorkflowAsync(definition, cancellationToken);
    var validationErrors = (await workflowValidator.ValidateAsync(workflowGraph.Workflow, cancellationToken)).ToList();
    
    if (validationErrors.Any())
        return new(false, validationErrors, new([]));
    

    发布前先将工作流定义从 JSON 字符串物化为完整的运行时对象(WorkflowGraph),再用 IWorkflowValidator 验证。如果有验证错误,直接返回失败。

  • 第二步:发出前置通知并清除旧的发布状态

    // Services/WorkflowDefinitionPublisher.cs 第93行
    await mediator.SendAsync(new WorkflowDefinitionPublishing(definition), cancellationToken);
    
    foreach (var publishedAndOrLatestWorkflow in publishedWorkflows)
    {
        var isPublished = publishedAndOrLatestWorkflow.IsPublished;
        publishedAndOrLatestWorkflow.IsPublished = false;
        publishedAndOrLatestWorkflow.IsLatest = false;
        await workflowDefinitionStore.SaveAsync(publishedAndOrLatestWorkflow, cancellationToken);
    
        if (isPublished)
            await mediator.SendAsync(new WorkflowDefinitionVersionRetracted(publishedAndOrLatestWorkflow), cancellationToken);
    }
    
    • WorkflowDefinitionPublishing 通知的消费者是 EvictWorkflowDefinitionServiceCache,在发布动作正式执行前先驱逐缓存。对之前已发布的版本发出 WorkflowDefinitionVersionRetracted 通知,消费者 RefreshActivityRegistryHandlers/Notifications/RefreshActivityRegistry.cs)会根据该工作流是否开启 UsableAsActivity 来更新活动注册表。
  • 第三步:标记新版本上线

    // Services/WorkflowDefinitionPublisher.cs 第115行
    definition.IsPublished = true;
    definition.IsLatest = true;
    definition = Initialize(definition);
    await workflowDefinitionStore.SaveAsync(definition, cancellationToken);
    
    var affectedWorkflows = new AffectedWorkflows(new List<WorkflowDefinition>());
    await mediator.SendAsync(new WorkflowDefinitionPublished(definition, affectedWorkflows), cancellationToken);
    
    • 发出 WorkflowDefinitionPublished 通知后,有多个消费者同时响应:

      • RefreshActivityRegistryHandlers/Notifications/RefreshActivityRegistry.cs 第25行):如果该工作流开启了 UsableAsActivity,调用 workflowDefinitionActivityRegistryUpdater.AddToRegistry() 将新版本注册为可调用子流程。
      • UpdateConsumingWorkflowsHandlers/Notifications/UpdateConsumingWorkflows.cs):触发 WorkflowReferenceUpdater 级联更新所有引用了此工作流的其他工作流。
      • IndexTriggersElsa.Workflows.Runtime/Handlers/IndexTriggers.cs):调用 triggerIndexer.IndexTriggersAsync() 对新发布的工作流进行触发器索引(详见运行时篇)。

    整个发布过程可以用一句话描述:先验证合法性,再把老版本全部下架,最后把新版本挂牌上线并广播通知。


六、撤回发布:让工作流临时下线

  • 撤回入口是 WorkflowDefinitionPublisher.RetractAsync() 方法(Services/WorkflowDefinitionPublisher.cs 第138行):

    // Services/WorkflowDefinitionPublisher.cs 第143行
    definition.IsPublished = false;
    
    await mediator.SendAsync(new WorkflowDefinitionRetracting(definition), cancellationToken);
    await workflowDefinitionStore.SaveAsync(definition, cancellationToken);
    await mediator.SendAsync(new WorkflowDefinitionRetracted(definition), cancellationToken);
    
  • WorkflowDefinitionRetracting 通知的消费者(EvictWorkflowDefinitionServiceCache)会立即清除缓存。WorkflowDefinitionRetracted 通知的消费者有两个:RefreshActivityRegistry 更新活动注册表;IndexTriggers 重新执行触发器索引,将该工作流的触发器从可匹配列表中清除,两者的目的都是确保撤回后不再响应新的触发请求。

    撤回不删除任何版本记录,也不影响 IsLatest 标志,版本历史完整保留。


七、版本回退:把老版本变成新版本

  • 回退入口是 WorkflowDefinitionPublisher.RevertVersionAsync() 方法(Services/WorkflowDefinitionPublisher.cs 第151行),由 WorkflowDefinitionManager.RevertVersionAsync() 转发调用:

    // Services/WorkflowDefinitionPublisher.cs 第160行
    if (latestVersion != null)
    {
        latestVersion.IsLatest = false;
        await workflowDefinitionStore.SaveAsync(latestVersion, cancellationToken);
    }
    var draft = await GetDraftAsync(definitionId, VersionOptions.SpecificVersion(version), cancellationToken);
    draft!.Id = identityGenerator.GenerateId(); // 新 Id(string 类型)
    draft.Version = (latestVersion?.Version ?? 0) + 1; // 版本号继续向后++
    draft.IsLatest = true;
    await workflowDefinitionStore.SaveAsync(draft, cancellationToken);
    

    回退的逻辑是"创建老版本的副本作为新的最新版本",而不是真正意义上的"回到过去"。操作完成后数据库里多一条新记录,内容与历史版本相同,但版本号是当前最高值 +1。这种设计保证了历史记录永远不会被篡改。


八、删除:有层次的删除操作

  • 删除操作有三个粒度,由 WorkflowDefinitionManager 统一管理(Services/WorkflowDefinitionManager.cs)。

    • 按 DefinitionId 全量删除(第16行):删除某个工作流的所有版本。

      // Services/WorkflowDefinitionManager.cs 第18行
      await notificationSender.SendAsync(new WorkflowDefinitionDeleting(definitionId), cancellationToken);
      var count = await store.DeleteAsync(filter, cancellationToken);
      await notificationSender.SendAsync(new WorkflowDefinitionDeleted(definitionId), cancellationToken);
      
      • WorkflowDefinitionDeleting 通知的消费者是 DeleteWorkflowInstancesHandlers/Notifications/DeleteWorkflowInstances.cs),它在删除数据库记录之前,先批量删除该工作流名下的所有运行实例workflowInstanceManager.BulkDeleteAsync()),同时删除缓存。WorkflowDefinitionDeleted 通知的消费者是 RefreshActivityRegistry,它负责从活动注册表中完整移除该工作流的所有版本。
    • 按版本 Id 删除单个版本DeleteVersionAsync,第96行):删除前若该版本是 IsPublished = true,会先调用 RetractAsync() 将其撤下。删除后调用私有方法 EnsureLastVersionIsLatestAsync(),找到剩余最高版本把它的 IsLatest 置为 true,确保始终有一条记录作为"最新版本"。

    • 批量删除BulkDeleteByDefinitionIdsAsyncBulkDeleteByIdsAsync):支持同时删除多个定义或多个版本 ID,发出对应的批量通知,逻辑与单个删除类似。

九、物化:从数据库记录变成可执行对象

"物化"是这个模块最核心的技术概念,理解了物化就理解了工作流定义和运行时之间的桥梁。数据库里存的 WorkflowDefinition 只是一段 JSON 字符串(活动树的序列化结果)。引擎要运行一个工作流,需要的是内存中活生生的 C# 对象树——带有活动实例、端口连接、节点引用的 WorkflowGraph。从 JSON 字符串变成 WorkflowGraph 的过程,就叫物化。

PS:这里用“物化”一词真的不是为了装波一,而是 Materialized 这个单词与 PostgreSQL 里的物化视图(Materialized View)一致...

  • 物化的入口是 WorkflowDefinitionService.MaterializeWorkflowAsync() 方法(Services/WorkflowDefinitionService.cs 第20行):

    // Services/WorkflowDefinitionService.cs 第20行
    public async Task<WorkflowGraph> MaterializeWorkflowAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
    {
        var materializer = materializerRegistry.GetMaterializer(definition.MaterializerName);
    
        if (materializer == null)
            throw new WorkflowMaterializerNotFoundException(definition.MaterializerName);
    
        var workflow = await materializer.MaterializeAsync(definition, cancellationToken);
        return await workflowGraphBuilder.BuildAsync(workflow, cancellationToken);
    }
    
  • 两种物化器:

    • JsonWorkflowMaterializerMaterializers/JsonWorkflowMaterializer.cs)是最常用的,直接用 WorkflowDefinitionMapperWorkflowDefinition.StringData(JSON)映射为 Workflow 对象,对应 UI 设计器创建的工作流。

    • ClrWorkflowMaterializerMaterializers/ClrWorkflowMaterializer.cs)用于代码方式定义的工作流(实现了 IWorkflow 接口的 C# 类)。它通过 ActivatorUtilities 实例化对应类,调用 BuildWorkflowAsync 构建工作流结构。

无论哪种物化器,最终都会调用 IWorkflowGraphBuilder.BuildAsync()Workflow 对象转换为一棵有父子关系的活动节点树(ActivityNode),它是能被引擎真正使用的对象。

十、查找工作流定义:查找或查找后转化成物化后的结果

  • 查找定义的逻辑集中在 WorkflowDefinitionServiceServices/WorkflowDefinitionService.cs)。
// Services/WorkflowDefinitionService.cs 第46行
public async Task<WorkflowDefinition?> FindWorkflowDefinitionAsync(WorkflowDefinitionHandle handle, CancellationToken cancellationToken = default)
{
    var filter = handle.ToFilter();
    return await workflowDefinitionStore.FindAsync(filter, cancellationToken);
}
  • WorkflowDefinitionHandle 支持两种定位方式:ByDefinitionId(definitionId, versionOptions) 按逻辑 ID + Options查找,ByDefinitionVersionId(versionId) 按具体版本 ID 直接定位。

  • FindWorkflowGraphAsync() 系列方法在找到定义后会进一步物化,直接返回可执行的 WorkflowGraph

  • TryFindWorkflowGraphAsync() 系列则返回 WorkflowGraphFindResult(同时包含定义和图),即使物化器不可用也不抛异常,方便调用方判断。

Elsa 还提供了启用了二级缓存的包装服务 CachingWorkflowDefinitionServiceServices/CachingWorkflowDefinitionService.cs),通过 IMemoryCache 缓存物化结果,并用 ChangeToken 机制自动失效。

十一、实例管理:运行记录的创建与保存

工作流"运行起来"之后,每一次运行都是一个"实例"(WorkflowInstance)。实例的管理由 WorkflowInstanceManager 负责(Services/WorkflowInstanceManager.cs)。

  • 创建实例:真正创建实例对象的是 WorkflowInstanceFactory.CreateWorkflowInstance()

    // Services/WorkflowInstanceFactory.cs
    public WorkflowInstance CreateWorkflowInstance(Workflow workflow, WorkflowInstanceOptions? options = null)
    {
        return new WorkflowInstance
        {
            Id = options?.WorkflowInstanceId ?? identityGenerator.GenerateId(), // string 类型
            DefinitionId = workflow.Identity.DefinitionId,
            DefinitionVersionId = workflow.Identity.Id,
            Status = WorkflowStatus.Running,
            SubStatus = WorkflowSubStatus.Pending,
            // ...
        };
    }
    
  • 保存实例WorkflowInstanceManager 提供了多个 SaveAsync() 重载,最终都写入 IWorkflowInstanceStore,然后发出 WorkflowInstanceSaved 通知:

    // Services/WorkflowInstanceManager.cs 第47行
    public async Task SaveAsync(WorkflowInstance workflowInstance, CancellationToken cancellationToken = default)
    {
        await store.SaveAsync(workflowInstance, cancellationToken);
        await notificationSender.SendAsync(new WorkflowInstanceSaved(workflowInstance), cancellationToken);
    }
    
    • WorkflowInstanceSaved 通知的消费者是 SignalBookmarkQueueWorkerElsa.Workflows.Runtime/Handlers/SignalBookmarkQueueWorker.cs),它调用 IBookmarkQueueSignaler.TriggerAsync() 唤醒书签队列工作者,驱动工作流继续执行。

十二、变量管理

  • WorkflowInstanceVariableManagerServices/WorkflowInstanceVariableManager.cs)提供了从外部读写工作流实例变量的能力。

  • 读变量的过程:先从 IWorkflowInstanceStore 找到实例,取出 WorkflowState;再物化出工作流图;然后调用 WorkflowExecutionContext.CreateAsync() 将状态快照"还原"回运行时上下文对象;最后委托给 IWorkflowInstanceVariableReader.GetVariables() 从上下文中读取变量值。

  • 写变量的过程:先还原上下文,调用 IWorkflowInstanceVariableWriter.SetVariables() 修改内存,最后再通过 WorkflowInstanceManager.SaveAsync(workflowExecutionContext) 将修改后的状态重新持久化回数据库。

变量并不单独存储,它们是 WorkflowState 的一部分,读写变量本质上就是反序列化→修改内存→再序列化的过程。

十三、引用更新:工作流引用另一子流程时的级联维护

Elsa 很好的支撑了一个复杂场景:当工作流 A 作为子流程被工作流 B 引用,A 发布了新版本后,B 可以自动更新对 A 的版本引用。UsableAsActivity属性用来控制是否将当前工作流作为子流程对外暴露。引用子流程时,是否总是使用子流程的最新版可以通过 AutoUpdateConsumingWorkflows 属性进行控制。

触发时机WorkflowDefinitionPublished 通知的消费者 UpdateConsumingWorkflowsHandlers/Notifications/UpdateConsumingWorkflows.cs)会调用此服务,触发条件是该工作流 Options.UsableAsActivity = trueOptions.AutoUpdateConsumingWorkflows = true

执行逻辑:通过 IWorkflowReferenceGraphBuilder.BuildGraphAsync() 构建引用关系图,对图做拓扑排序,确保在多层依赖时能按正确顺序处理。对每个需要更新的工作流,找到活动树中所有指向目标工作流的 WorkflowDefinitionActivity 节点,将它们的版本引用更新为最新发布版本。最后,根据该工作流更新前是否已发布,决定调用 PublishAsync() 直接发布或 SaveDraftAsync() 保存为草稿。

十四、事件通知

当前章节的内容全部再在上文执行过程中提及过,这里只是从事件的视角重新回顾一下。

1. 清理工作流定义缓存:EvictWorkflowDefinitionServiceCache

  • 目的:让与该工作流定义相关的缓存立即失效,避免系统后续读取旧版本的工作流定义、旧版本的物化结果或旧的查询结果。
  • 触发时机:
    • 工作流定义保存草稿时(WorkflowDefinitionDraftSaving
    • 工作流定义发布前时(WorkflowDefinitionPublishing
    • 工作流定义撤回前时(WorkflowDefinitionRetracting
    • 工作流定义删除前时(WorkflowDefinitionDeleting
    • 批量删除工作流定义前时(WorkflowDefinitionsDeleting
    • 批量更新工作流定义版本时(WorkflowDefinitionVersionsUpdating

2. 刷新缓存中有效子流程:RefreshActivityRegistry

  • 目的: 保持 “工作流是否能作为子流程被其他工作流引用” 这一份注册表与当前最新状态一致。用来保证设计器、运行时或引用方工作流在查找“可用工作流活动”时,能拿到最新结果,而不会看到已经下线或已删除的子流程。

  • 触发时机:

    • 工作流定义发布后(WorkflowDefinitionPublished
    • 工作流定义撤回后(WorkflowDefinitionRetracted
    • 某个工作流定义版本撤回后(WorkflowDefinitionVersionRetracted
    • 工作流定义删除后(WorkflowDefinitionDeleted
    • 批量删除工作流定义后(WorkflowDefinitionsDeleted
    • 某个工作流定义版本删除后(WorkflowDefinitionVersionDeleted
    • 批量删除工作流定义版本后(WorkflowDefinitionVersionsDeleted
    • 批量更新工作流定义版本后(WorkflowDefinitionVersionsUpdated
  • 实现过程:
    当工作流发布或版本更新后,处理器会检查该定义的 Options.UsableAsActivity

    • 如果该工作流允许被当作子流程使用,就调用注册表更新器把它加入注册表。
    • 如果该工作流不再允许作为子流程使用,或者已经被撤回/删除,则把它从注册表中移除。
    • 对于批量事件,则循环处理每一个定义或版本。

3. 级联删除工作流实例:DeleteWorkflowInstances

  • 目的:
    在定义或版本被删除前,把依赖它们的运行实例一并清理掉,防止留下无效实例、脏数据或悬空引用。

  • 触发时机:

    • 删除整个工作流定义前(WorkflowDefinitionDeleting
    • 删除某个工作流定义版本前(WorkflowDefinitionVersionDeleting
    • 批量删除工作流定义前(WorkflowDefinitionsDeleting
    • 批量删除工作流定义版本前(WorkflowDefinitionVersionsDeleting
  • 实现过程:

    • 这个处理器会根据不同事件构造不同的 WorkflowInstanceFilter
      • 删除整个定义时,用 DefinitionId 过滤实例
      • 删除某个版本时,用 DefinitionVersionId 过滤实例
      • 批量删除时,用 DefinitionIdsIds 过滤实例集合
    • 然后统一调用 _workflowInstanceManager.BulkDeleteAsync(...) 执行批量删除。

4. 更新引用当前工作流的父流程:UpdateConsumingWorkflows

  • 目的: 当某个工作流被重新发布后,把所有“引用了这个工作流”的其他工作流一并更新,确保引用关系与最新发布版本保持一致。

  • 触发时机:

    • 工作流定义发布后(WorkflowDefinitionPublished
  • 实现过程:

    • 处理器拿到刚刚发布的 WorkflowDefinition 后,会调用IWorkflowReferenceUpdater.UpdateWorkflowReferencesAsync(...)。 这个过程会查找哪些工作流依赖当前定义,并把这些引用关系更新到最新状态。
    • 更新完成后,受影响的工作流会被放入 notification.AffectedWorkflows.WorkflowDefinitions 集合中,方便后续流程继续使用这些变更结果。

5. 校验工作流定义:ValidateWorkflow

  • 触发时机:

    • 工作流定义进入校验阶段时(WorkflowDefinitionValidating
  • 事件类名:
    WorkflowDefinitionValidating

  • 目的:
    在工作流真正保存、发布或继续后续处理之前,先做基础合法性检查,尽早拦截明显错误。

  • 实现过程:

    • 当前这个处理器主要检查两类内容:
      • Inputs 中是否存在重名项
      • Outputs 中是否存在重名项
    • 它会把同名项分组统计,如果发现有重复名称,就生成错误消息并写入 notification.ValidationErrors。 这样后续流程就可以根据这些校验错误决定是否阻止发布或提示用户修复配置问题。

十五、缓存管理

  • WorkflowDefinitionCacheManagerServices/WorkflowDefinitionCacheManager.cs)负责工作流定义的内存缓存策略。缓存键设计考虑了多租户场景,所有缓存键都包含租户 ID 前缀,确保不同租户的工作流定义在缓存中完全隔离。

  • 缓存失效通过 ChangeToken 机制实现:当工作流定义发生变更时,调用 EvictWorkflowDefinitionAsync() 触发令牌失效,所有监听了该令牌的缓存条目会自动失效,无需手动枚举删除。

  • CachingWorkflowDefinitionService 包装 WorkflowDefinitionService,在 FindWorkflowGraphAsync() 时先查内存缓存,命中则直接返回,未命中则调用内部服务物化并存入缓存(缓存条目绑定到对应工作流定义的 ChangeToken,定义变更时自动失效)。


十六、整体执行流程总结

1. 创建草稿
   WorkflowDefinitionPublisher.New() / NewAsync()
   → 生成 DefinitionId (string) 和 Id (string),序列化根活动为 JSON
   → 得到 IsPublished=false, IsLatest=true 的内存对象

2. 保存草稿(可能多次保存)
   WorkflowDefinitionPublisher.SaveDraftAsync()
   → 版本号永远向后++,维护 IsLatest 标志
   → mediator.SendAsync(WorkflowDefinitionDraftSaving)
     └─ EvictWorkflowDefinitionServiceCache → 缓存失效
   → 写入 IWorkflowDefinitionStore
   → mediator.SendAsync(WorkflowDefinitionDraftSaved)

3. 发布上线
   WorkflowDefinitionPublisher.PublishAsync()
   → 物化+验证(MaterializeWorkflowAsync → IWorkflowValidator)
   → mediator.SendAsync(WorkflowDefinitionPublishing) → 缓存失效
   → 清除旧版本 IsPublished/IsLatest,发出 WorkflowDefinitionVersionRetracted → RefreshActivityRegistry 更新注册表
   → 设置当前版本 IsPublished=true, IsLatest=true
   → mediator.SendAsync(WorkflowDefinitionPublished)
     ├─ RefreshActivityRegistry → 注册为可调用活动(UsableAsActivity=true)
     ├─ UpdateConsumingWorkflows → 级联更新引用方工作流
     └─ IndexTriggers (Runtime模块) → 触发器索引

4. 运行时使用
   WorkflowDefinitionService.FindWorkflowGraphAsync()
   → 先查 CachingWorkflowDefinitionService 缓存
   → 物化为 WorkflowGraph
   → WorkflowRuntime 创建执行上下文并运行
   → 保存实例时发出 WorkflowInstanceSaved
     └─ SignalBookmarkQueueWorker → 唤醒书签队列

下一篇:Elsa.Workflows.Core — 执行引擎核心详解

posted @ 2026-04-12 13:42  叨奈特挖井人  阅读(1)  评论(0)    收藏  举报