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

  1. void HelloWorldSample()   
  2. {   
  3.     string message = "Hello World";   
  4.     Console.WriteLine(message);   
  5. }

在这个例子中我们建立了一个message变量,它的生命周期就是我们的HelloWorldSample这个函数,在函数开始时我们会对这个变量进行初始化,结束时会释放这个变量的资源。同时我们调用了一个叫做WriteLine的函数,这个函数有一个输入参数类型是string,在调用函数时我们将变量message作为具体的值传入WriteLine函数。如果将上面的代码转化为Workflow,你会发现这些概念是对应的。

Workflow Hello World

  1. Sequence seq = new Sequence()
  2. {
  3.     DisplayName = "HelloWorldSample",
  4.     Variables =    
  5.     {   
  6.         new Variable<string>()   
  7.         {   
  8.             Name = "message",   
  9.             Default = "Hello World",   
  10.         },   
  11.     },
  12.     Activities =   
  13.     {   
  14.         new WriteLine()   
  15.         {   
  16.             Text = new InArgument<string>(new VisualBasicValue<string>("[message]")),   
  17.         }   
  18.     }
  19. };

下面我们来具体介绍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

  1. public sealed class Increment : CodeActivity

  2. {

  3.     public InArgument<int> Input { get; set; }

  4.     public OutArgument<int> Output { get; set; }

  5.     protected override void Execute(CodeActivityContext context)

  6.     {

  7.         this.Output.Set(context, this.Input.Get(context) + 1);

  8.     }

  9. }

  10. public sealed class Increment : CodeActivity

  11. {

  12.     public InOutArgument<int> Value { get; set; }

  13.     protected override void Execute(CodeActivityContext context)

  14.     {

  15.         this.Value.Set(context, this.Value.Get(context) + 1);

  16.     }

  17. }

变量(Variable),Workflow中的数据公共存储单元

Variable是用于存放数据的单元,Variable分为普通变量(Variable)实现变量(Implementation Variable)两种。

生命周期:等同于定义它的Activity,在Activity开始执行时, Workflow Runtime会初始化所有定义在这个Activity中的变量,而在Activity结束时结束Variable的生命周期。

注意:

Variable的初始化是有顺序的,如果你不清楚当前 Activity对Variable的初始化顺序,不建议使用同一个Activity中定义的不同Variable进行相互的初始化。因为这种错误无法在设计时别检测,只有在运行时才能被发现。

一个错误的例子

  1. Sequence sequence = new Sequence()
  2. {
  3.     Variables =   
  4.     {   
  5.         new Variable<string>()   
  6.         {   
  7.             Name = "var1",   
  8.             Default = new VisualBasicValue<string>("[var2]"),   
  9.         },                 
  10.         new Variable<string>()   
  11.         {   
  12.             Name = "var2",   
  13.             Default = new VisualBasicValue<string>("\"Hello\""),   
  14.         },   
  15.     },
  16. };

 

 

因为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赋值

  1. Sequence seq = new Sequence()   
  2. {   
  3.     Variables =   
  4.     {   
  5.         new Variable<int>()   
  6.         {   
  7.             Name = "data",   
  8.             Default = new VisualBasicValue<int>("123"),   
  9.             Modifiers = VariableModifiers.ReadOnly,   
  10.         }   
  11.     },   
  12.   
  13.     Activities =   
  14.     {   
  15.         new Assign<int>()   
  16.         {   
  17.             To = new VisualBasicReference<int>("[data]"),   
  18.             Value = new VisualBasicValue<int>("345"),   
  19.         }   
  20.     },   
  21. };

以上Workflow会在运行时Assign时获得一个InvalidOperationException。

映射(Mapped)

被标志为Mapped属性的Variable,可在工作流实例持久化时对这个Variable进行查询,在此我就不详细展开了。

普通变量(Variable)

一个典型的例子是Sequence得Variables属性。

可见域:定义Variable的Activity的所有普通子活动(Child Activity)。

注意:

你是无法在Activity的内部函数中或其实现子活动(Implementation Child Activity)中使用当前Activity所定义的普通变量。

下面是一个在自定义活动中使用Variable的例子:

范例

  1. public sealed class WriteVariable : NativeActivity

  2. {

  3.     private Collection<Variable> variables;

  4.     public Collection<Variable> Variables

  5.     {

  6.         get

  7.         {

  8.             if (this.variables == null)

  9.             {

  10.                 this.variables = new Collection<Variable>();

  11.             }

  12.             return this.variables;

  13.         }

  14.     }

  15.     protected override void CacheMetadata(NativeActivityMetadata metadata)

  16.     {

  17.         metadata.SetVariablesCollection(this.Variables);

  18.         base.CacheMetadata(metadata);

  19.     }

  20.     protected override void Execute(NativeActivityContext context)

  21.     {

  22.         foreach (Variable var in this.Variables)

  23.         {

  24.             Console.WriteLine(var.Name);

  25.             //注意:公共变量是无法在定义它的Activity中使用的

  26.             //因此你无法执行

  27.             //Console.WriteLine(var.Get(context));   

  28.         }

  29.     }

  30. }

实现变量(Implementation Variable)

Implantation Variable用于储存Activity的私有数据,它只能在定义它的Activity的内部函数中或Activity的实现子活动(Implementation Child Activity)中使用。

可见域:定义Variable的Activity的内部函数和所有实现子活动(Implementation Child Activity)。

下面是一个使用Implementation Variable的例子:

范例

  1. public sealed class SimpleSequence : NativeActivity

  2. {

  3.     Collection<Activity> activities;

  4.     Variable<int> lastIndex;

  5.     CompletionCallback onChildComplete;

  6.     public SimpleSequence()

  7.         : base()

  8.     {

  9.         this.lastIndex = new Variable<int>();

  10.         this.onChildComplete = new CompletionCallback(ChildComplete);

  11.     }

  12.     public Collection<Activity> Activities

  13.     {

  14.         get

  15.         {

  16.             if (this.activities == null)

  17.             {

  18.                 this.activities = new Collection<Activity>();

  19.             }

  20.             return this.activities;

  21.         }

  22.     }

  23.     protected override void CacheMetadata(NativeActivityMetadata metadata)

  24.     {

  25.         metadata.SetChildrenCollection(this.Activities);

  26.         metadata.AddImplementationVariable(this.lastIndex);

  27.     }

  28.     protected override void Execute(NativeActivityContext context)

  29.     {

  30.         if (this.activities != null && this.Activities.Count > 0)

  31.         {

  32.             context.ScheduleActivity(this.Activities[0], this.onChildComplete);

  33.         }

  34.     }

  35.     void ChildComplete(NativeActivityContext context, ActivityInstance completedInstance)

  36.     {

  37.         int nextChildIndex = this.lastIndex.Get(context) + 1;

  38.         if (nextChildIndex < this.Activities.Count)

  39.         {

  40.             context.ScheduleActivity(this.Activities[nextChildIndex], this.onChildComplete);

  41.             this.lastIndex.Set(context, nextChildIndex);

  42.         }

  43.     }

  44. }