通过


工作流扩展

可以扩展Microsoft Dataverse 中使用的工作流设计器中可用的选项。 通过添加包含拓展 CodeActivity 类的程序集来添加这些扩展。 这些扩展通常称为 工作流程序集工作流活动

可以在工作流设计器、自定义操作和对话框(已弃用)中使用这些自定义扩展。

重要

尽可能首先考虑应用多个声明性选项之一来定义业务逻辑。 有关详细信息,请参阅 在 Dataverse 中应用业务逻辑

当声明性进程不符合要求时,请使用工作流扩展。

何时创建工作流扩展

如果找不到使用默认进程活动所需的功能,请添加自定义活动,以便在编辑器中使用这些活动来撰写工作流、对话框和操作进程。

默认情况下,这些进程包括一组可以执行的常见活动,如下表所示:

活动 Workflow Action Dialog
查询数据 X
赋值 X X
创建记录 X X X
更新记录 X X X
分配记录 X X X
发送电子邮件 X X X
启动子工作流 X X X
执行操作 X X
链接子对话 X
更改状态 X X X
停止工作流 X X
停止对话框 X

使用 “执行操作” 活动执行任何自定义操作或名为 “命令操作”的以下系统消息:

AddToQueue

AddUserToRecordTeam

RemoveUserFromRecordTeam

SetProcess

SetWordTemplate

如果你有 Dynamics 365 Sales 或服务解决方案,则可以根据解决方案找到其他命令操作:

ApplyRoutingRule

CalculateActualValue

CloseOpportunity

GetQuoteProductsFromOpportunity

GetSalesOrderProductsFromOpportunity

LockInvoicePricing

LockSalesOrderPricing

QualifyLead

RemoveUserFromRecordTeam

ResolveIncident

ResolveQuote

Revise

UnlockInvoicePricing

UnlockSalesOrderPricing

有关详细信息,请参见:

使用的技术

可以使用.NET Framework 活动库(该库定义了自定义活动)来注册生成的程序集。 这些活动显示在 Web 应用程序编辑器中,并在进程运行时调用。

自定义工作流活动需要创建一个包含派生自抽象 CodeActivity 类的一个或多个类的 .NET Framework 程序集。 此类提供了 Execute(CodeActivityContext) 方法,该方法将在 Dataverse 平台上运行活动时被调用。 程序集中的每个类都定义了一个特定的功能。

工作流活动应定义在进程设计器中可见的输入和输出参数。 这些参数使某人能够将数据传入工作流活动并接收已处理的输出。 编写类时,为这些参数添加属性,并用 .NET 属性 标注它们,为 Dataverse 提供在设计器中展示您自定义工作流活动及其参数所需的元数据。

创建自定义工作流活动程序集

这些步骤介绍如何使用 Visual Studio 创建自定义工作流活动。 有关完整的分步示例,请参阅 教程:创建工作流扩展

  1. 创建面向 .NET Framework 4.6.2 的类库项目。

    重要

    虽然使用更高版本生成的程序集通常可以正常工作,但如果程序集使用 4.6.2 后引入的任何功能,则会发生错误。

  2. 安装 Microsoft.CrmSdk.Workflow NuGet 包。

    此包包括 Microsoft.CrmSdk.CoreAssemblies 包。

  3. (可选)如果要使用早期绑定表类,请在项目中包括它们。

    有关详细信息,请参见:

  4. 添加公共类。 类的名称应与活动执行的操作相对应。

  5. 添加以下 using 指令。

    using System.Activities;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;
    
  6. 将属性添加到类以表示任何输入或输出参数。 使用 .NET 属性 提供必要的元数据,以向工作流进程设计器公开这些属性。

    有关详细信息,请参阅 “添加参数”。

  7. 使类派生自 CodeActivity 类 ,并实现包含活动执行的操作的 Execute(CodeActivityContext) 方法

    有关详细信息,请参阅 将代码添加到 Execute 方法

  8. 对程序集进行签名。

  9. 构建程序集。

  10. 使用插件注册工具注册程序集。 设置NameWorkflowActivityGroupName属性以定义工作流设计器显示的文本。

    有关详细信息,请参阅注册您的程序集

  11. 通过在工作流、对话框或操作流程中调用该工作流活动来对其进行测试。

  12. (推荐)将工作流活动添加到解决方案。

添加参数

定义类的参数时,将其定义为 InArgument<T>OutArgument<T>InOutArgument<T> 类型。 这些类型提供从通用 参数类 继承的方法来获取或设置参数。 在 Execute 方法中,代码使用了这些方法。 有关详细信息,请参阅 将代码添加到 Execute 方法

自定义工作流活动使用输入或输出参数时,将相应的 .NET 属性添加到定义它们的公共类属性。 进程设计器读取此数据以定义如何在进程设计器中设置参数。

可以将以下类型的属性用作输入或输出参数:

int

输入和输出参数

若要定义要在进程设计器中为输入或输出参数显示的文本,请将以下模式与 .NET 属性一起使用:

[Input("Integer input")]
public InArgument<int> IntInput { get; set; }

[Output("Integer output")]
public OutArgument<int> IntOutput { get; set; }

类中的单个属性既可以是输入和输出参数,也可以包括这两个属性:

[Input("Int input")]  
[Output("Int output")]  
public InOutArgument<int> IntParameter { get; set; }

必需值

若要在流程中使用工作流活动时将输入参数设为必需,请使用[RequiredArgument]属性。

默认值

将值作为输入参数传递或设置输出参数而不定义值时,请指定默认值。 例如,以下代码设置布尔属性的默认值:

[Input("Bool input")]
[Default("True")]
public InArgument<bool> Bool { get; set; }

默认值的格式取决于属性的类型。 下表中提供了示例:

类型 示例
bool [Default(“True”)]
日期时间 [Default(“2004-07-09T02:54:00Z”)]
十进制 [Default(“23.45”)]
[Default(“23.45”)]
Money [Default(“23.45”)]
EntityReference [Default(“3B036E3E-94F9-DE11-B508-00155DBA2902”,“账户”)]
int [Default(“23”)]
OptionSetValue [Default(“3”)]
字符串 [Default(“string default”)]

EntityReference 参数

为参数定义属性 EntityReference 时,请使用该 ReferenceTarget 属性。 此属性建立允许的表类型。 例如:

[Input("EntityReference input")]
[Output("EntityReference output")]
[ReferenceTarget("account")]
public InOutArgument<EntityReference> AccountReference { get; set; }

OptionSetValue 参数

为参数定义属性 OptionSetValue 时,请使用该 AttributeTarget 属性。 此属性定义哪个表和列包含参数的有效值集。 例如:

[Input("Account IndustryCode value")]
[AttributeTarget("account", "industrycode")]
[Default("3")]
public InArgument<OptionSetValue> IndustryCode { get; set; }

将代码添加到 Execute 方法

CodeActivity.Execute(CodeActivityContext) 方法方法中包含的逻辑定义工作流活动的作用。

重要

Execute 方法中编写代码,确保其为无状态。 不要使用全局变量或成员变量将数据从一个调用传递到下一个调用。 为了提高性能,Dataverse 缓存自定义工作流活动实例。 由于这种缓存机制,并非每次调用自定义工作流活动时都会调用构造函数。 此外,多个系统线程可以同时执行自定义工作流活动。 仅使用通过 CodeActivityContext 参数传递给 Execute 该方法的信息。

引用参数

若要引用为类定义的参数,请使用 Argument.GetArgument.Set(ActivityContext, Object) 方法。 这些方法需要使用传递给该方法的 CodeActivityContext 实例。 以下示例演示如何访问输入参数的值并设置输出参数的值。

using Microsoft.Xrm.Sdk.Workflow;
using System.Activities;

namespace SampleWorkflowActivity
{
  public class IncrementByTen : CodeActivity
  {
    [RequiredArgument]
    [Input("Decimal input")]
    public InArgument<decimal> DecInput { get; set; }

    [Output("Decimal output")]
    public OutArgument<decimal> DecOutput { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
      decimal input = DecInput.Get(context);
      DecOutput.Set(context, input + 10);
    }
  }
}

获取上下文信息

当代码需要上下文信息时,请使用带<接口的 IWorkflowContext访问它。 此对象派生自 IExecutionContext 接口,该接口提供对描述操作上下文的许多只读属性的访问权限。 IWorkflowContext 提供了与使用您的工作流程序集的执行工作流相关的类似上下文信息。

在您的Execute函数中使用以下代码来访问IWorkflowContext

protected override void Execute(CodeActivityContext context)
{
 IWorkflowContext workflowContext = context.GetExtension<IWorkflowContext>();
...

重要

不要根据上下文信息包含任何逻辑依赖项。 当某人在工作流中使用自定义工作流活动时,他们应设置设计器中的所有相关输入参数。 自定义活动的输出值或行为应始终由输入参数决定,以便没有更改行为的隐藏因素。 当某人在设计器中使用自定义活动时,行为应始终可预测。

使用适用于 .NET 的 SDK

当您需要使用 SDK for .NET 执行数据操作时,请使用 CodeActivityContext.GetExtension<T> 方法,并通过 IOrganizationServiceFactory 接口访问。 在此处,使用 CreateOrganizationService(Nullable<Guid>) 该方法访问可用于执行数据操作的服务代理的实例。 IWorkflowContext属性InitiatingUserId可用于确定要在调用进程所在的同一上下文中执行操作时要使用的用户上下文。 使用函数中的 Execute 以下代码获取对组织服务的访问权限:

protected override void Execute(CodeActivityContext context)
{
 IWorkflowContext workflowContext = context.GetExtension<IWorkflowContext>();
 IOrganizationServiceFactory serviceFactory = context.GetExtension<IOrganizationServiceFactory>();

 // Use the context service to create an instance of IOrganizationService.             
 IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.InitiatingUserId);
...

注册程序集

使用插件注册工具(PRT)注册包含自定义工作流活动的程序集。 此工具与注册插件时使用的工具相同。对于插件和自定义工作流活动,必须注册程序集以将其上传到环境。 但是,不需要为定制工作流活动注册步骤。

对于自定义工作流活动,请指定以下属性来控制工作流进程设计器显示的内容。

领域 说明
Description 在进程设计器的 UI 中不可见,但从存储此信息的 PluginType 表提取的数据生成文档时,它可能很有用。
FriendlyName 插件的用户友好名称。
Name 表示的菜单的名称。
WorkflowActivityGroupName 添加到 Dataverse 进程设计器中主菜单的子菜单的名称。

设置描述性属性。

注释

测试工作流活动时,这些值在非托管解决方案中不可见。 但是,导出包含此工作流活动的托管解决方案时,这些值在进程设计器中可见。

调试工作流活动

将自定义工作流活动部署到 Dataverse 时,您可以捕获配置文件以便进行本地调试回放,并使用跟踪服务将信息写入表中。

以下示例演示如何使用跟踪服务编写消息: Add your message.

protected override void Execute(CodeActivityContext context)
{
//Create the tracing service
ITracingService tracingService = context.GetExtension<ITracingService>();

//Use the tracing service
tracingService.Trace("{0} {1} {2}.", "Add", "your", "message");
...

有关详细信息,请参见:

添加到解决方案

使用插件注册工具注册程序集时,请将其添加到 默认解决方案。 不要将此解决方案与 Common Data Service 默认解决方案混淆。 由于 默认解决方案 包含应用于环境的所有非托管自定义项,因此在使用解决方案分发自定义工作流活动之前,必须将它添加到非托管解决方案。 例如,可以将它添加到 Common Data Service 默认解决方案 或任何已创建的非托管解决方案。

管理对自定义工作流活动的更改

你需要维护自定义工作流活动的代码。 由于代码更改可能包括重大变更,因此需要管理此更改。 使用不同的步骤更新或升级自定义工作流程序集。

注册包含自定义工作流活动的程序集时,将包含程序集的版本号。 注册工具使用程序集中的反射提取此信息。 可以使用 Visual Studio 项目中的文件来控制版本号 AssemblyInfo.cs

可在底部找到如下所示的分区:

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
//[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

此版本信息非常重要,因为它允许在想要包含新功能时将更新应用到已部署的程序集或升级程序集。

更新自定义工作流活动程序集

修复 bug 或重构代码而不对公共类或方法签名进行重大更改时,请更新程序集,以便所有正在运行的进程都使用新版本的程序集自动启动。

更新程序集

  1. 仅修改 AssemblyInfo.csAssemblyVersion 属性中的 Build NumberRevision 值。 例如,从 1.0.0.0 更改为 1.0.10.5.
  2. 使用插件注册工具更新程序集。 有关详细信息,请参阅 更新程序集

升级自定义工作流活动程序集

如果所做的更改包括对公共类或方法签名的重大更改(例如更改参数),则中断定义为使用原始签名的任何当前正在运行的进程。 在这种情况下,必须升级组件。 此操作将创建一个新的自定义工作流活动,该活动公开用于定义要在进程设计器中应用的版本的选项。 此版本控制允许重新配置使用此活动的每个进程以适应新程序集中包含的更改。 使用原始程序集的所有进程更新为使用升级的程序集后,可以取消注册旧程序集。

升级程序集

  1. 确保新程序集的 NamePublicKeyTokenCulture 与现有程序集相同。

  2. 更改属性中的AssemblyInfo.cs和/或AssemblyVersion值。 例如,从 1.0.0.0 更改为 2.0.0.0.

  3. 使用插件注册工具将程序集注册为新程序集。 有关详细信息,请参阅 注册程序集

  4. 对于使用自定义工作流活动的每个进程,请停用该过程并编辑使用自定义工作流活动的步骤。

    可以在进程设计器中找到 一个版本 选择器,该选择器可用于选择应使用哪个版本的程序集。

    工作流版本设置。

将所有进程转换为使用新程序集时,请使用插件注册工具取消注册程序集,使其不再可用。 有关详细信息,请参阅 “注销组件”。

性能指南

工作流扩展的性能注意事项与普通插件的性能注意事项相同。有关详细信息,请参阅 “分析插件性能”。

与普通插件不同,工作流扩展不提供为特定步骤显式注册代码的机会。 你无法控制工作流扩展中的代码是同步运行还是异步运行。 同步运行的代码需要特别小心,因为它直接影响应用程序用户体验。

作为可重用组件,可以将工作流扩展添加到任何工作流或自定义操作。 可以将工作流配置为 实时 工作流,这意味着它以同步方式运行。 自定义操作始终是同步操作,但除非设置了 “启用回滚”,否则它们不会参与数据库事务。

重要

当工作流扩展在同步工作流或自定义操作中运行时,运行代码所花费的时间直接影响用户体验。 因此,同步使用时,工作流扩展应该不超过两秒才能完成。 如果扩展需要超过此时间,请记录此限制,并阻止在同步工作流或自定义操作中使用扩展。

请注意,在参与事务的同步工作流或自定义操作中,工作流扩展引发的任何错误都会导致整个事务回滚。 此回滚操作是一项昂贵的操作,可能会影响性能。

使用 .IWorkflowContext属性中的WorkflowMode值来确定工作流是否同步运行。

实时工作流阶段

在实时(同步)工作流中使用工作流扩展时,事件执行管道在特定阶段调用扩展。 下表显示了这些阶段。 有关详细信息,请参阅 事件执行管道

消息 阶段
创建 PostOperation
删除 PreOperation
更新 PreOperation 或
PostOperation

使用IWorkflowContext.StageName属性中的值检测阶段。

对于 更新 操作,可以使用工作流设计器中的 “前 ”或“ ”选项来配置阶段。 有关详细信息,请参阅 使用实时工作流

如果工作流扩展依赖于在执行上下文中传递的数据,运行阶段将决定数据是否在IWorkflowContext.InputParametersIWorkflowContext.OutputParameters中可用。

注释

不要包含基于 InputParametersOutputParameters 的逻辑依赖项。 工作流扩展应依赖于配置的 输入和输出参数 ,以便使用工作流扩展的人员可以理解预期行为,而无需隐藏任何内容。

工作流扩展的实体图像

无法为工作流扩展配置实体映像。 你只注册程序集,工作流活动在工作流的上下文中运行。 可通过分别使用键值 PreBusinessEntityPostBusinessEntity 获取工作流扩展的实体映像(用于预操作和后操作)。 有关详细信息,请参阅 实体映像

另见

有关插件和工作流开发的最佳做法和指南
教程:创建工作流扩展
示例:创建自定义工作流活动
示例:使用自定义工作流活动更新下一个生日
示例:使用自定义工作流活动计算信用评分