C# 表达式

从 .NET Framework 4.5 开始,Windows Workflow Foundation (WF) 支持 C# 表达式。 在 Visual Studio 2012 中创建的面向 .NET Framework 4.5 的新 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# 表达式。 在 Visual Studio 2012 中创建的面向 .NET Framework 4.5 的 C# 工作流项目使用 C# 表达式,而 Visual Basic 工作流项目则使用 Visual Basic 表达式。 要指定所需的 C# 表达式,可将其键入标有“输入 C# 表达式”标签的框中。 该标签显示在属性窗口中(当在设计器中选中活动时),或显示在工作流设计器的活动上。 下例中,在 WriteLine 范围内,一个 Sequence 中包含了两个 NoPersistScope 活动。

Screenshot that shows an automatically created sequence activity.

注意

C# 表达式仅在 Visual Studio 中得到支持,在重新承载的工作流设计器中不予支持。 有关重新承载的设计器支持的新 WF45 功能的更多信息,请参阅重新承载的工作流设计器支持的新 Workflow Foundation 4.5 功能

向后兼容性

在现有已迁移到 .NET Framework 4.6.1 的 .NET Framework 4 C# 工作流项目中使用的 Visual Basic 表达式是得到支持的。 当在工作流设计器中查看 Visual Basic 表达式时,如果 Visual Basic 表达不符合 C# 语法,则现有 Visual Basic 文本将被替换成“在 XAML 中设置了值”。 如果 Visual Basic 表达式符合 C# 语法则予以显示。 若要将 Visual Basic 表达式更新为 C#,可在工作流设计器中编辑这些表达式,指定等效的 C# 表达式。 将 Visual Basic 表达式更新为 C# 不是必须的,但一旦在工作流设计器中进行更新,这些表达式即转换成 C# 并不可重新转换为 Visual Basic。

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

基于 .NET Framework 4.6.1 代码的工作流支持 C# 表达式,但 C# 表达式须用 TextExpressionCompiler.Compile 进行编译,然后才能调用工作流。 工作流作者可以用 CSharpValue 表示表达式的右值,用 CSharpReference 表示表达式的左值。 在下例中,用一个 Assign 活动以及 WriteLine 活动中所包含的 Sequence 活动创建了一个工作流。 为 CSharpReferenceTo 自变量指定了一个 Assign,用来表示表达式的左值。 为 CSharpValueValue 自变量和 AssignText 自变量指定了一个 WriteLine,用于表示这两个表达式的右值。

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);

工作流构造完毕后,通过调用 CompileExpressions 帮助器方法编译了 C# 表达式,随后调用工作流。 下面的示例为 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# 表达式未编译,则调用工作流时将引发 NotSupportedException,并显示如下类似消息:Expression 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 编写的默认工作流类型,而在 Visual Studio 中面向 .NET Framework 4.6.1 创建的 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 和从 XAML 序列化工作流和活动

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

XAMLX 工作流服务支持 C# 表达式。 当工作流服务承载于 IIS 或 WAS 中时,无须执行任何额外步骤,但如果 XAML 工作流服务是自承载的,则 C# 表达式必须经过编译。 要在自承载的 XAMLX 工作流服务中编译 C# 表达式,应先将 XAMLX 文件加载到 WorkflowService 中,然后将 WorkflowServiceBody 传递给前面在代码工作流中使用 C# 表达式部分中所描述的 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);
}