Workflow 4.0中的Variable和Argument
还是那句老话,Workflow 4.0和Workflow 3.0/3.5相比有很大的不同,其中之一就是Activity间数据交换方式的变化。
在3.0/3.5中,我们使用DependenceProperty,使用绑定的方式在不同的Property建立关联,由此实现数据的交换。这种方式的确很灵活,但是也带来不少弊病。比如所有的数据都存在同一个域中导致性能的下降,同时DependenceProperty的生命周期也是很让人困惑的问题,我不只一次的在论坛上听到有人提出对DependenceProperty的初始化时间提出疑问。
在4.0中,我们建立了一种更简洁的数据管理模式,变量(Variable)和参数(Argument)。这也是一种更符合大部分人编程习惯的数据模型。在传统的编程模式中,我们使用变量来存储数据,使用函数来封装程序的功能,使用参数和返回值来进行函数间的数据交换。
在Workflow中,Activity是封装功能的单元,我们可以理解为传统编程中的函数。而变量、参数的概念是类似的。我们来看一个例子。
一个C#的Hello World只有几行
C#的Hello World
- void HelloWorldSample()
- {
- string message = "Hello World";
- Console.WriteLine(message);
- }
在这个例子中我们建立了一个message变量,它的生命周期就是我们的HelloWorldSample这个函数,在函数开始时我们会对这个变量进行初始化,结束时会释放这个变量的资源。同时我们调用了一个叫做WriteLine的函数,这个函数有一个输入参数类型是string,在调用函数时我们将变量message作为具体的值传入WriteLine函数。如果将上面的代码转化为Workflow,你会发现这些概念是对应的。
Workflow Hello World
- Sequence seq = new Sequence()
- {
- DisplayName = "HelloWorldSample",
- Variables =
- {
- new Variable<string>()
- {
- Name = "message",
- Default = "Hello World",
- },
- },
- Activities =
- {
- new WriteLine()
- {
- Text = new InArgument<string>(new VisualBasicValue<string>("[message]")),
- }
- }
- };
下面我们来具体介绍Variable和Argument。
参数(Argument),Activity的I/O数据流接口
Argument是Activity的I/O接口,分为InArgument、OutArgument、InOutArgument。
可见域:定义Argument的Activity的Execute函数及其所有实现子活动(Implementation Child Activity )。
生命周期:等同于定义它的Activity。
InArgument
用于向Activity传入数据,只能对其进行取值操作。它可以使用Activity<T>、常数、Lambda Expression或者Variable作为表达式。
OutArgument
用于向Activity外部传出数据,只能对其进行赋值操作。它可以使用Activity<Location<T>>、Lambda Expression或者Variable作为表达式。
InOutArgument
用于向Activity传入或传出数据,可对其进行取值和赋值操作。它可以使用Activity<Location<T>>、Lambda Expression或者Variable作为表达式。
RequiredArgumentAttribute
默认情况下,在使用Activity时不需要对每一个Argument赋值,但是你可以为你的 Argument加上RequiredArgumentAttribute,用于表示必须在定义Workflow时必须为这个Argument赋初值。Runtime在创建Workflow实例时会对所有Argument进行检测,如果某个RequiredArgument没有赋值,将会抛出ValidationException。
范例:使用Argument
public sealed class Increment : CodeActivity
{
public InArgument<int> Input { get; set; }
public OutArgument<int> Output { get; set; }
protected override void Execute(CodeActivityContext context)
{
this.Output.Set(context, this.Input.Get(context) + 1);
}
}
public sealed class Increment : CodeActivity
{
public InOutArgument<int> Value { get; set; }
protected override void Execute(CodeActivityContext context)
{
this.Value.Set(context, this.Value.Get(context) + 1);
}
}
变量(Variable),Workflow中的数据公共存储单元
Variable是用于存放数据的单元,Variable分为普通变量(Variable)实现变量(Implementation Variable)两种。
生命周期:等同于定义它的Activity,在Activity开始执行时, Workflow Runtime会初始化所有定义在这个Activity中的变量,而在Activity结束时结束Variable的生命周期。
注意:
Variable的初始化是有顺序的,如果你不清楚当前 Activity对Variable的初始化顺序,不建议使用同一个Activity中定义的不同Variable进行相互的初始化。因为这种错误无法在设计时别检测,只有在运行时才能被发现。
一个错误的例子
- Sequence sequence = new Sequence()
- {
- Variables =
- {
- new Variable<string>()
- {
- Name = "var1",
- Default = new VisualBasicValue<string>("[var2]"),
- },
- new Variable<string>()
- {
- Name = "var2",
- Default = new VisualBasicValue<string>("\"Hello\""),
- },
- },
- };
因为var2在var1之后初始化,所以在运行时会得到异常说var1没有被初始化。但是如果你交换var1和var2的位置,就不会得到任何异常。
Variable的默认值的类型是System.Activites.Activity<T>。但你还可以用常数、Lambda Expression或者其他得Variable来初始化Variable。除了类型(Type),名称(Name)和默认值(DefaultValue),Variable还有两个附加属性:只读(ReadOnly)和映射(Mapped)
只读(ReadOnly)
ReadOnly属性顾名思义,就是表示这个Variable在能在初始化时对其赋值,若试图在之后对其赋值,将会得到一个运行时的异常。
对ReadOnly的Variable赋值
- Sequence seq = new Sequence()
- {
- Variables =
- {
- new Variable<int>()
- {
- Name = "data",
- Default = new VisualBasicValue<int>("123"),
- Modifiers = VariableModifiers.ReadOnly,
- }
- },
- Activities =
- {
- new Assign<int>()
- {
- To = new VisualBasicReference<int>("[data]"),
- Value = new VisualBasicValue<int>("345"),
- }
- },
- };
以上Workflow会在运行时Assign时获得一个InvalidOperationException。
映射(Mapped)
被标志为Mapped属性的Variable,可在工作流实例持久化时对这个Variable进行查询,在此我就不详细展开了。
普通变量(Variable)
一个典型的例子是Sequence得Variables属性。
可见域:定义Variable的Activity的所有普通子活动(Child Activity)。
注意:
你是无法在Activity的内部函数中或其实现子活动(Implementation Child Activity)中使用当前Activity所定义的普通变量。
下面是一个在自定义活动中使用Variable的例子:
范例
public sealed class WriteVariable : NativeActivity
{
private Collection<Variable> variables;
public Collection<Variable> Variables
{
get
{
if (this.variables == null)
{
this.variables = new Collection<Variable>();
}
return this.variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetVariablesCollection(this.Variables);
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
foreach (Variable var in this.Variables)
{
Console.WriteLine(var.Name);
//注意:公共变量是无法在定义它的Activity中使用的
//因此你无法执行
//Console.WriteLine(var.Get(context));
}
}
}
实现变量(Implementation Variable)
Implantation Variable用于储存Activity的私有数据,它只能在定义它的Activity的内部函数中或Activity的实现子活动(Implementation Child Activity)中使用。
可见域:定义Variable的Activity的内部函数和所有实现子活动(Implementation Child Activity)。
下面是一个使用Implementation Variable的例子:
范例
public sealed class SimpleSequence : NativeActivity
{
Collection<Activity> activities;
Variable<int> lastIndex;
CompletionCallback onChildComplete;
public SimpleSequence()
: base()
{
this.lastIndex = new Variable<int>();
this.onChildComplete = new CompletionCallback(ChildComplete);
}
public Collection<Activity> Activities
{
get
{
if (this.activities == null)
{
this.activities = new Collection<Activity>();
}
return this.activities;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetChildrenCollection(this.Activities);
metadata.AddImplementationVariable(this.lastIndex);
}
protected override void Execute(NativeActivityContext context)
{
if (this.activities != null && this.Activities.Count > 0)
{
context.ScheduleActivity(this.Activities[0], this.onChildComplete);
}
}
void ChildComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
int nextChildIndex = this.lastIndex.Get(context) + 1;
if (nextChildIndex < this.Activities.Count)
{
context.ScheduleActivity(this.Activities[nextChildIndex], this.onChildComplete);
this.lastIndex.Set(context, nextChildIndex);
}
}
}