从 .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 值。 为CSharpValue
的Value
参数和Assign
的Text
参数指定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# 表达式,则当使用类似于以下内容的消息调用工作流时,将抛出一个 NotSupportedException:Expression Activity type 'CSharpValue
1' 需要编译才能运行。 请确保已编译工作流。”
如果您的自定义代码工作流使用了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 'CSharpValue
1' 需要编译才能运行。 请确保已编译工作流。”
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
中,然后将Body
和WorkflowService
的实例传递给在之前的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);
}