使用命令性代码创作工作流
本主题适用于 Windows Workflow Foundation 4。
工作流定义是已配置活动对象的树。这种活动树有多种定义方法,包括手动编辑 XAML 或使用工作流设计器来生成 XAML。但是,并非必须使用 XAML。工作流定义也可以通过编程方式来创建。本主题概述如何使用代码来创建工作流定义。
创建工作流定义
通过实例化活动类型的实例以及配置活动对象的属性可创建工作流定义。对于不包含子活动的活动而言,可使用若干代码行来完成。
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
注意: |
---|
本主题中的示例使用 WorkflowInvoker 运行示例工作流。有关调用工作流、传递参数以及可用的不同承载选择的更多信息,请参见使用 WorkflowInvoker 和 WorkflowApplication。 |
在本示例中,将要创建由单个 WriteLine 活动组成的工作流。还要设置 WriteLine 活动的 Text 参数,并调用该工作流。如果活动包含子活动,构造方法类似。以下示例使用一个包含两个 WriteLine 活动的 Sequence 活动。
Activity wf = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Hello"
},
new WriteLine
{
Text = "World."
}
}
};
WorkflowInvoker.Invoke(wf);
使用对象初始值设定项
本主题中的示例使用对象初始化语法。对于用代码创建工作流定义而言,对象初始化语法非常有用,因为它为工作流中的活动提供了分层视图,可以显示活动之间的关系。通过编程方式创建工作流时,不要求必须使用对象初始化语法。下面的示例与前面的示例在功能上是等效的。
WriteLine hello = new WriteLine();
hello.Text = "Hello";
WriteLine world = new WriteLine();
world.Text = "World";
Sequence wf = new Sequence();
wf.Activities.Add(hello);
wf.Activities.Add(world);
WorkflowInvoker.Invoke(wf);
有关对象初始值设定项的更多信息,请参见 How to: Initialize Objects without Calling a Constructor (C# Programming Guide)(如何:在不调用构造函数的情况下初始化对象(C# 编程指南))和 How to: Declare an Object by Using an Object Initializer(如何:使用对象初始值设定项来声明对象)。
使用变量、文本值和表达式
使用代码创建工作流定义时,请注意哪些代码是作为创建工作流定义的一部分来执行,哪些代码是作为该工作流实例执行的一部分来执行。例如,以下工作流将生成一个随机数,并将其写入控制台。
Variable<int> n = new Variable<int>
{
Name = "n"
};
Activity wf = new Sequence
{
Variables = { n },
Activities =
{
new Assign<int>
{
To = n,
Value = new Random().Next(1, 101)
},
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
}
};
执行此工作流定义代码时,将会调用 Random.Next
并将结果以文本值的形式保存在工作流定义中。可以调用此工作流的多个实例,全部实例将显示相同的数字。若要在工作流执行过程中生成随机数,必需使用一个表达式,以便在每次运行工作流时计算该表达式。
new Assign<int>
{
To = n,
Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}
VisualBasicValue 表示 Visual Basic 语法中的表达式,可在表达式中用 R 值表示,在每次执行包含活动时进行计算。将表达式的结果赋给工作流变量 n
,工作流中的下一个活动将使用这些结果。若要在运行时访问工作流变量 n
的值,需要 ActivityContext。可使用以下 lambda 表达式进行访问。
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
Lambda 表达式不可序列化为 XAML 格式。若要使此表达式与 XAML 相兼容,请使用 ExpressionServices 和 Convert,如下例所示。
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
}
还可以使用 VisualBasicValue。
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
//Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
Text = new VisualBasicValue<string>("\"The number is \" + n.ToString()")
}
有关表达式的更多信息,请参见表达式。
参数与动态活动
通过将活动组合到活动树中并配置各参数和属性的方式可创建以代码编写的工作流定义。可绑定现有参数,但无法将新参数添加到活动中。这包括传递给根活动的工作流参数。在命令性代码中,工作流参数被指定为新 CLR 类型的属性,而在 XAML 中,它们将使用 x:Class 和 x:Member 来声明。将工作流定义创建为内存中对象的树时,由于没有创建新的 CLR 类型,因此无法添加参数。但是,可将参数添加到 DynamicActivity。在本示例中,将要创建一个 DynamicActivity,它接受两个整型参数并将它们相加,然后返回结果。为每个参数创建一个 DynamicActivityProperty,并将运算结果赋给 DynamicActivity 的 Result 参数。
InArgument<int> Operand1 = new InArgument<int>();
InArgument<int> Operand2 = new InArgument<int>();
DynamicActivity<int> wf = new DynamicActivity<int>
{
Properties =
{
new DynamicActivityProperty
{
Name = "Operand1",
Type = typeof(InArgument<int>),
Value = Operand1
},
new DynamicActivityProperty
{
Name = "Operand2",
Type = typeof(InArgument<int>),
Value = Operand2
}
},
Implementation = () => new Sequence
{
Activities =
{
new Assign<int>
{
To = new ArgumentReference<int> { ArgumentName = "Result" },
Value = new InArgument<int>((env) => Operand1.Get(env) + Operand2.Get(env))
}
}
}
};
Dictionary<string, object> wfParams = new Dictionary<string, object>
{
{ "Operand1", 25 },
{ "Operand2", 15 }
};
int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);
有关动态活动的更多信息,请参见使用 DynamicActivity 在运行时创建活动。