WorkflowApplication 应用中变量、参数、书签

本文介绍了如何使用C#代码创建Windows Workflow Foundation (WF) 应用程序,包括创建简单的工作流、使用InArguments、OutArguments和InOutArguments参数,以及在工作流中使用变量和书签。示例代码展示了如何创建和运行工作流,以及如何进行异步执行和数据交互。重点讲解了参数和变量在工作流中的作用,以及书签在传递数据给工作流时的功能。

使用C#代码来创建WF应用程序

1:创建ConsoleApplication项目:

创建一个控制台项目,命名为HelloCodeWorkflow.

2: 添加对System.Activities程序集的引用

默认的,一个新的控制台程序并不会应用System.Activities程序集。

 

3:编写定义Workflow的代码。

using System.Activities;

using System.Activities.Statements;

namespace HelloCodeWorkflow {

   class Program {

       static void Main(string[] args) {

           WorkflowInvoker.Invoke(new HelloWorkflow());

       }

    }

   public class HelloWorkflow:Activity {

       public HelloWorkflow() {

           this.Implementation = () => new Sequence {

                Activities = {

                    newWriteLine(){Text="Hello Workflow"}

                }

           };

       }

    }

}

4:运行它:

设置HelloCodeWorkflow作为启动项目,按Ctrl+F5,和期望的一样,结果和前面的结果一样。

 

它是怎么工作的。。。

我们使用下面的命名空间:

using System.Activities;

using System.Activities.Statements;

 

因为WorkflowInvoker类属于System.Activities命名空间,Sequence活动,WriteLine活动属于System.Activities.Statements命名空间。

 

public class HelloWorkflow:Activity {

   public HelloWorkflow() {

       this.Implementation = () => new Sequence {

           Activities = {

               newWriteLine(){Text="Hellow Workflow"}

           }

       };

    }

}

 

通过实现继承自Activity的一个类,我们通过C#代码定义了Workflow

 

WorkflowInvoker.Invoke(s);

 

上面的代码加载了一个工作流实例,并且自动的运行它,WorkflowInvoker.Invoke方法是同步的,这也就是说它使用调用者的线程来执行workflow.

 

还有更多。。

 

WF4 也提供了一个类DynamicActivity,通过它我们可以在运行时动态的来定义工作流实例,换句话说,通过使用DynamicActivity,我们没有必要在工作流的初始化之前就定义工作流。下面是一些样例代码:

public static DynamicActivity GetWF() {

   return new DynamicActivity() {

       Implementation = () => new Sequence() {

           Activities ={

               newWriteLine(){Text="Hello Workflow"}

           }

       }

   };

}

 

 

使用InArguments 来初始化WF程序。

在这个任务中,我们创建一个可以接收参数的工作流程序,我们使用InArguments来定义工作流需要的参数。

 

创建工作流程序

1:创建工作流程序:

创建一个新的工作流控制台程序,命名为UseInArgument

 

2:制作WF程序。

在这里定义了工作流需要的两个参数,参数的方向是In(输入).类型是String.

接着使用WriteLine方法输出这两个参数。

 

3:编写代码启动工作流。

修改Program.cs 代码如下:

 

using System.Activities;

using System.Activities.Statements;

namespace UseInArgument {

class Program {

       static void Main(string[] args) {

           WorkflowInvoker.Invoke(new Workflow1()

           {

                FirstName="Andrew",

                SecondName="Zhu"

           });

       }

    }

}

 

4:运行:

设置UseInArgument作为启动项目,按Ctrl+F5,结果如下:

 

它是怎么工作的。。。

考虑下我们在Main方法中的语句:

FirstName="Andrew"

 

FirstName的类型是InArgument,但是我们怎么可以不通过显示的转换就可以将String赋值给InArgumen呢?,这是因为InArgument使用了

System.ComponentModel.TypeConverterAttribute(System.Activities.XamlIntegration.InArgumentConverter)特性来标记的。

就是InArgumentConverter使得可以直接将string赋值给InArgument类型。如果你想了解更多关于TypeConverter,可以查看MSDN的:

http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

 

还有更多:

WF3/3.5 中,给一个Wrorkflow传递参数是通过Dictionary<T>类的,这种方式在WF4中也可以。

using System.Activities;

using System.Activities.Statements;

using System.Collections.Generic;

namespace UseInArgument {

   class Program {

       static void Main(string[] args) {

           IDictionary<string, object> inputDictionary =

                new Dictionary<string,object>()

           {

               {"FirstName","Andrew"},

                {"SecondName","Zhu"}

           };

           WorkflowInvoker.Invoke(new Workflow1(),

                                  inputDictionary);

       }

    }

}

 

如果你是使用C#代码来创建的工作流,我们可以像下面这样使用InArgument参数

public classWorkflowInCode : Activity

{

    public InArgument<string>FirstName { get; set;}

    public InArgument<string>SecondName { get; set;}

 

    publicWorkflowInCode()

    {

        this.Implementation= () =>new Sequence()

        {

            Activities =

            {

                newWriteLine(){ Text=newInArgument<string>(

                    activityContext=>"My name is "+FirstName.Get(activityContext))},

                newWriteLine(){Text=newInArgument<string>(

                   activityContext=>SecondName.Get(activityContext))}

            }

        };

    }

}

 

 

创建一个使用OutArgument参数的工作流程序

1:创建工作流控制台程序,命名为UseOutArgument

2:制作WF程序:

在这里,定义了一个OutMessage的输出参数。使用A-B Assign活动来将OutMessage设置为”This is a message.”

 

3:编写调用WF的代码:

using System;

using System.Activities;

using System.Collections.Generic;

namespace UseOutArgument {

   class Program {

       static void Main(string[] args) {

           IDictionary<string,object> output=

                WorkflowInvoker.Invoke(newWorkflow1());

           Console.WriteLine(output["OutMessage"]);

       }

    }

}

4:设置UseOutArgument作为启动项目,按Ctrl+F5运行程序,结果如下:

 

它是怎样工作的。。。

看看下面的代码片段:

IDictionary<string,object> output=

    WorkflowInvoker.Invoke(new Workflow1());

Console.WriteLine(output["OutMessage"]);

 

OutMessage是我们在Workflow1.xaml中定义的输出参数

(OutArgument),WorkflowInvoker.Invoke方法返回一个IDictionary类型的对象,里面包含了工作流的输出参数。

 

还有更多...

还有第三种类型:InOutArgument,它用来绑定一个活动的输入和输出参数,在大部分的情况下,我们可以使用InOutArgument来代替InArgumentOutArgument,但是他们还是有一些不同的地方---例如,我们不能直接将一个String赋值给InOutArgument,但是却可以直接将String赋值给InArgument

 

 

创建一个使用InOutArgumentWF程序

1:创建工作流控制台程序:UseInOutArgument

2:制作工作流程序:

Workflow包含一个InOutArgumentInOutMessage“,WriteLine活动输出参数,Assign活动为InOutMessage赋值。

3:修改Main函数如下:

class Program

 {

     static void Main(string[]args)

     {

         IDictionary<string,object>input = newDictionary<string,object>()

         {

             {"InOutMessage","Now,I am InMessage"}

         };

 

         IDictionary<string,object>output =

             WorkflowInvoker.Invoke(newWorkflow1(),input);

 

         Console.WriteLine(output["InOutMessage"]);

         Console.ReadLine();

     }

 }

4:运行,

设置UseInOutArgument作为启动项目按Ctrl+F5 运行,结果如下:

 

它是怎么工作的。。。

下面的代码定义了输入参数:

IDictionary<string, object> input =

                new Dictionary<string,object>()

           {

               {"InOutMessage","Now, I am InMessage"}

           };

下面的代码返回工作流程序的结果:

IDictionary<string,object> output=

                WorkflowInvoker.Invoke(newWorkflow1(),input);

 

还有更多。。。

我们不能直接将String赋值给InOutArgument,所以下面的代码是不允许的:

IDictionary<string, object> output =

   WorkflowInvoker.Invoke(new Workflow1()

    {

       InOutMessage="Now,I am InMessage"

});

 

 

WF程序中使用变量

 

我们可以在WF程序运行的时候使用一些变量来临时的保存一些值,在这个任务中,我们创建一个在控制台中循环输出5个数字的WF程序,我们将使用NumberCounter变量来作为计数器。

 

1:创建工作流控制台程序:UseVariable.

2:添加一个Sequence活动,然后点击Sequence活动,创建一个Int32类型的NumberCounter变量,设置变量的ScopeSequence,然后按下面的截图制作workflow,在第二个Assign活动中输入NumberCounter= NumberCounter +1.

3:运行:

设置UseVariable为启动项目,按Ctrl+F5运行,结果如下:

 

它是怎样工作的。。。

为了让workflow的逻辑更容易理解,可以转换工作流为C#代码,如下:

int NumberCounter = 0;

do

{

   Console.WriteLine(NumberCounter);

   NumberCounter++;

}while (NumberCounter <= 5);

 

虽然我们可以使用参数(arguments)来将数据流入流出工作流,我们也可以使用变量(Variable)来储存在workflow中的一些临时数据,每一个variable都有它的作用域,只有作用域内的活动可以操作这些变量。

 

还有更多。。。

请明白,我们不能在外部的宿主环境中访问到工作流中定义的变量,WF4的变量主要是在工作流的实例之间临时的保存数据使用的。要在外部宿主中访问工作流我们可以使用书签(Bookmark)

 

异步的运行WF程序

 

在前面的实例中,我们使用WorkflowInvoker.Invoke方法来同步的启动工作流,它很容易使用,但是有些时候,尤其是在真实的应用程序中,一个工作流应该运行在另一个独立的线程中,在这个示例中,我们使用WorkflowApplication来运行一个工作流。

 

1:创建工作流控制台程序,命名为UseWorkflowApplication.

2:制作workflow

打开Workflow1.xaml的设计器,点击Arguments,创建两个Int32InArguments,名称为Number1Number2,接着创建一个Int32OutArgument,名称为Result。添加一个Assign活动。输入Result = Number1 + Number2

 

3Main函数如下:

class Program

{

    static void Main(string[]args)

    {

        IDictionary<string,object>output =

            WorkflowInvoker.Invoke(newWorkflow1() {Number1 = 123, Number2 = 456 });

        Console.WriteLine(output["Result"]);

 

        AutoResetEventsyncEvent =new AutoResetEvent(false);

        IDictionary<string,object>input =

            newDictionary<string,object>()

            {

                {"Number1",123},

                {"Number2",456}

            };

        IDictionary<string,object>output2 = null;

        WorkflowApplicationwfApp =new WorkflowApplication(

            newWorkflow1(), input);

        wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgse)

        {

            Console.WriteLine("Workflow thread id:" +Thread.CurrentThread.ManagedThreadId);

            output2 = e.Outputs;

            syncEvent.Set();

        };

        wfApp.Run();

        Console.WriteLine("workflow start execute");

        syncEvent.WaitOne();

        Console.WriteLine(output2["Result"]);

        Console.WriteLine("Host thread id:" +Thread.CurrentThread.ManagedThreadId);

 

        Console.ReadLine();

    }

}

4:运行:结果如下:

 

 

 

它怎样工作。。。

AutoResetEvent syncEvent = newAutoResetEvent(false);

 

当工作流线程和控制台主线程同时运行的时候,主线程可能会在工作流线程结束前终止,为了防止这种情况发生,我们使用AutoResetEvent来对主线程和工作流线程进行同步

syncEvent.WaitOne();

上面的语句,会让主线程一直等待,直到工作流syncEvnet set的时候。

wfApp.Completed =

delegate(WorkflowApplicationCompletedEventArgse)

{

  output = e.Outputs;

  syncEvent.Set();

};

 

通过匿名委托,在wfApp Completed事件发生的时候,就会调用syncEventSet方法了。

 

 

自定义一个使用书签(Bookmark)MyReadLine活动

 

通过使用InArgumenOutArgumentInOutArgument,我们可以在工作流启动和结束的时候流动数据(输入数据给工作流,从工作流中获取数据作为结果),但是怎样可以在工作流运行的时候传递数据给工作流呢? --Bookmark可以帮助我们实现这个功能,在这个示例中,我们将创建一个使用BookmarkMyReadLine的活动

 

1:创建工作流控制台项目,命名为UseBookmark,接着添加一个MyReadLineActivity的类,

 

2:修改MyReadLineActivity的代码如下:

 

 

using System.Activities;

namespace UseBookmark{

   public class MyReadLine : NativeActivity<string>{

       [RequiredArgument]

       public InArgument<string> BookmarkName { get; set; }

       protected override void Execute(

            NativeActivityContext context)

       {

           context.CreateBookmark(BookmarkName.Get(context),

                     newBookmarkCallback(OnResumeBookmark));

       }

       protected override bool CanInduceIdle

       {

           get

           {

                { return true;}

           }

       }

       public void OnResumeBookmark(

           NativeActivityContext context,

           Bookmark bookmark,

           object obj)

       {

           Result.Set(context, (string)obj);

       }

    }

}

 

保存文件,然后按F6,这样这个活动就可以出现在工作流设计器的工具箱中了。

(注意:MyReadLineActivity类是public的,否则工具箱中将找不到该活动)

 

3:创作工作流如下:

 

4:编写代码来宿主工作流:

class Program

{

    static void Main(string[]args)

    {

        AutoResetEventsyncEvent =new AutoResetEvent(false);

        stringbookmarkName = "GreetingBookmark";

        WorkflowApplicationwfApp =new WorkflowApplication(

            newWorkflow1() { BookmarkNameInArg =bookmarkName });

        wfApp.Completed = (completedEventArgs)=> { syncEvent.Set(); };

        wfApp.Run();

        Thread.Sleep(2000);

        wfApp.ResumeBookmark(bookmarkName,Console.ReadLine());

        syncEvent.WaitOne();

 

        Console.ReadLine();

    }

}

 

5:运行,结果如下:

 

它怎样工作。。。

 

在上面的第二步中,我们创建了一个从NativeActivity继承的类,NativeActivity是可以用来创造复杂活动的一个特殊的抽象活动,我们在Chapter 5Custom Activities中再讨论。

context.CreateBookmark(BookmarkName.Get(context),

       new BookmarkCallback(OnResumeBookmark));

这个语句,WF上下文创建了带BookmarkNameBookmarkCallbackBookmark,当wfApp.ResumeBookmark方法调用的时候,定义在自定义活动中的 OnResumeBookmark 方法就会被执行。

protected override bool CanInduceIdle{

   get

    {

       { return true;}

    }

}

这个是内建的属性,用来判断自定义工作流是否可以变得空闲状态(idle).默认值是false

考虑下step3 的下面的代码:

wfApp.ResumeBookmark(bookmarkName,

                Console.ReadLine());

当这条语句执行的时候,定义在MyReadLine活动中的 OnResumeBookmark方法就会被执行,并且接受Console.ReadLine()的值作为参数。

可以将上面的代码拆开来:

string resumeValue=Console.ReadLine();

wfApp.ResumeBookmark(bookmarkName,resumeValue);

小结:变量、参数、书签

1、  变量是用来临时保存是数据

2、  参数是用来传入传出数据

3、  BookMark是用于在工作流执行的时候传递数据给工作流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值