C# 表达式

从 .NET Framework 4.5 开始,Windows Workflow Foundation(WF)支持 C# 表达式。 在面向 .NET Framework 4.5 的 Visual Studio 2012 中创建的新 C# 工作流项目使用 C# 表达式,Visual Basic 工作流项目使用 Visual Basic 表达式。 使用 Visual Basic 表达式的现有 .NET Framework 4 工作流项目可以迁移到 .NET Framework 4.6.1,无论项目使用哪种语言,并且这些迁移是受到支持的。 本主题概述了 WF 中的 C# 表达式。

在工作流中使用 C# 表达式

在工作流设计器中使用 C# 表达式

从 .NET Framework 4.5 开始,Windows Workflow Foundation(WF)支持 C# 表达式。 在面向 .NET Framework 4.5 的 Visual Studio 2012 中创建的 C# 工作流项目使用 C# 表达式,而 Visual Basic 工作流项目则使用 Visual Basic 表达式。 若要指定所需的 C# 表达式,请在标记为 Enter a C# 表达式的框中键入它。 当在设计器中选择活动或工作流设计器中的活动时,此标签将显示在属性窗口中。 在下面的示例中,两个WriteLine活动包含在一个Sequence内部,而Sequence又在内部。

显示自动创建的序列活动的屏幕截图。

注释

C# 表达式仅在 Visual Studio 中受支持,在重新托管的工作流设计器中不受支持。 有关重新托管设计器中支持的新 WF45 功能的详细信息,请参阅 重新托管工作流设计器中对新 Workflow Foundation 4.5 功能的支持

向后兼容性

支持迁移到 .NET Framework 4.6.1 的现有 .NET Framework 4 C# 工作流项目中的 Visual Basic 表达式。 在工作流设计器中查看 Visual Basic 表达式时,现有 Visual Basic 表达式的文本将替换为 XAML 中设置的值,除非 Visual Basic 表达式是有效的 C# 语法。 如果 Visual Basic 表达式是有效的 C# 语法,则显示表达式。 若要将 Visual Basic 表达式更新为 C#,可以在工作流设计器中编辑它们,并指定等效的 C# 表达式。 不需要将 Visual Basic 表达式更新为 C#,但在工作流设计器中更新表达式后,它们将转换为 C# 并且可能不还原到 Visual Basic。

在代码工作流中使用 C# 表达式

基于 .NET Framework 4.6.1 代码的工作流支持 C# 表达式,但在调用工作流之前,必须使用 C# 表达式进行 TextExpressionCompiler.Compile编译。 工作流作者可用于 CSharpValue 表示表达式的 r 值,以及 CSharpReference 表示表达式的 l 值。 在下例中,用一个 Assign 活动以及 WriteLine 活动中所包含的 Sequence 活动创建了一个工作流。 为CSharpReference参数To指定 AAssign,并表示表达式的 l 值。 为CSharpValueValue参数和AssignText参数指定WriteLine,并表示这两个表达式的r值。

Variable<int> n = new Variable<int>
{
    Name = "n"
};

Activity wf = new Sequence
{
    Variables = { n },
    Activities =
    {
        new Assign<int>
        {
            To = new CSharpReference<int>("n"),
            Value = new CSharpValue<int>("new Random().Next(1, 101)")
        },
        new WriteLine
        {
            Text = new CSharpValue<string>("\"The number is \" + n")
        }
    }
};

CompileExpressions(wf);

WorkflowInvoker.Invoke(wf);

构造工作流后,C# 表达式通过调用 CompileExpressions 帮助程序方法进行编译,然后调用工作流。 下面的示例是CompileExpressions方法。

static void CompileExpressions(Activity activity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions.
    string activityName = activity.GetType().ToString();

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = activity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = false
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { activity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRoot(
        activity, compiledExpressionRoot);
}

注释

如果未编译 C# 表达式,则当使用类似于以下内容的消息调用工作流时,将抛出一个 NotSupportedExceptionExpression Activity type 'CSharpValue1' 需要编译才能运行。 请确保已编译工作流。”

如果您的自定义代码工作流使用了DynamicActivity,则需要对CompileExpressions方法进行一些更改,如以下代码示例所示。

static void CompileExpressions(DynamicActivity dynamicActivity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions. For Dynamic Activities this can be retrieved using the
    // name property , which must be in the form Namespace.Type.
    string activityName = dynamicActivity.Name;

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = dynamicActivity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = true
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { dynamicActivity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(
        dynamicActivity, compiledExpressionRoot);
}

在动态活动中编译 C# 表达式的 CompileExpressions 重载中有几处不同。

  • CompileExpressions 的参数是一个 DynamicActivity

  • 可以通过DynamicActivity.Name属性来检索类型名称和命名空间。

  • TextExpressionCompilerSettings.ForImplementation 设置为 true

  • 调用 CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation 而非 CompiledExpressionInvoker.SetCompiledExpressionRoot

有关在代码中使用表达式的详细信息,请参阅 使用命令性代码创作工作流、活动和表达式

在 XAML 工作流中使用 C# 表达式

XAML 工作流支持 C# 表达式。 编译的 XAML 工作流被编译为一种类型,松散的 XAML 工作流由运行时加载,并在执行工作流时编译为活动树。

编译型 Xaml

编译型 XAML 工作流支持 C# 表达式,此种工作流编译成类型,作为面向 .NET Framework 4.6.1 的 C# 工作流项目的组成部分。 编译的 XAML 是在 Visual Studio 中创建的默认工作流创作类型,在面向 .NET Framework 4.6.1 的 Visual Studio 中创建的 C# 工作流项目使用 C# 表达式。

宽松型 Xaml

松散 XAML 工作流支持 C# 表达式。 加载和调用松散 XAML 工作流的工作流宿主程序必须面向 .NET Framework 4.6.1,并且 CompileExpressions 必须设置为 true (默认值为 false)。 若要将 CompileExpressions 设置为 true,请创建一个 ActivityXamlServicesSettings 实例,其 CompileExpressions 属性设置为 true,并将其作为参数传递给 ActivityXamlServices.Load。 如果未将 CompileExpressions 设为 true,则将抛出一个 NotSupportedException,并附带如下内容的消息:Expression Activity type 'CSharpValue1' 需要编译才能运行。 请确保已编译工作流。”

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;

有关使用 XAML 工作流的详细信息,请参阅 对 XAML 的工作流和活动进行序列化

在 XAMLX 工作流服务中使用 C# 表达式

XAMLX 工作流服务支持 C# 表达式。 当工作流服务托管在 IIS 或 WAS 中时,无需执行其他步骤,但如果 XAML 工作流服务是自承载的,则必须编译 C# 表达式。 若要在自承载 XAMLX 工作流服务中编译 C# 表达式,请先将 XAMLX 文件加载到WorkflowService中,然后将BodyWorkflowService的实例传递给在之前的CompileExpressions部分描述的方法。 在以下示例中,加载 XAMLX 工作流服务、编译 C# 表达式,然后打开工作流服务并等待请求。

// Load the XAMLX workflow service.
WorkflowService workflow1 =
    (WorkflowService)XamlServices.Load(xamlxPath);

// Compile the C# expressions in the workflow by passing the Body to CompileExpressions.
CompileExpressions(workflow1.Body);

// Initialize the WorkflowServiceHost.
var host = new WorkflowServiceHost(workflow1, new Uri("http://localhost:8293/Service1.xamlx"));

// Enable Metadata publishing/
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);

// Open the WorkflowServiceHost and wait for requests.
host.Open();
Console.WriteLine("Press enter to quit");
Console.ReadLine();

如果未编译 C# 表达式,则 Open 作会成功,但调用工作流时将失败。 以下 CompileExpressions 方法与之前在 代码工作流部分中使用 C# 表达式 的方法相同。

static void CompileExpressions(Activity activity)
{
    // activityName is the Namespace.Type of the activity that contains the
    // C# expressions.
    string activityName = activity.GetType().ToString();

    // Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
    // to represent the new type that represents the compiled expressions.
    // Take everything after the last . for the type name.
    string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
    // Take everything before the last . for the namespace.
    string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());

    // Create a TextExpressionCompilerSettings.
    TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
    {
        Activity = activity,
        Language = "C#",
        ActivityName = activityType,
        ActivityNamespace = activityNamespace,
        RootNamespace = null,
        GenerateAsPartialClass = false,
        AlwaysGenerateSource = true,
        ForImplementation = false
    };

    // Compile the C# expression.
    TextExpressionCompilerResults results =
        new TextExpressionCompiler(settings).Compile();

    // Any compilation errors are contained in the CompilerMessages.
    if (results.HasErrors)
    {
        throw new Exception("Compilation failed.");
    }

    // Create an instance of the new compiled expression type.
    ICompiledExpressionRoot compiledExpressionRoot =
        Activator.CreateInstance(results.ResultType,
            new object[] { activity }) as ICompiledExpressionRoot;

    // Attach it to the activity.
    CompiledExpressionInvoker.SetCompiledExpressionRoot(
        activity, compiledExpressionRoot);
}