• Beginning WF4读书笔计 - 第二章 基于后台代码的工作流


        在上一章中,我们通过工作流引擎设计器成功的实现了一个简单的工作流示例。接下来我们将采用后台代码的方式来实现同样的一个流程。

    控制台程序

        首先创建一个控制台程序

     

        添加对“Systm.Activties”的引用(注:这个库在进行工作流开发时必须引用的)

        同时更改Program.cs中的命名空间如下:

    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.Activities.Expressions;

        并在main()函数中添加如下代码:

    WorkflowInvoker.Invoke(CreateWorkflow());
    Console.WriteLine("Press ENTER to exit");
    Console.ReadLine();

        此时,你会发现与第一章中的代码已经是大同小异了,只不过用CreateWorkflow函数代替了原来的new Workflow1()而已。不同的是Workflow1函数是由工作流设计器引擎自动生成的,而CreateWorkflow则要我们手工打造。

    设计工作流

        在上一章中,我们用工作流设计器设计时,我们可以简单的认为“workflow”其实是一个很很多的内嵌的“类”及期“属性”构成的集合。

        接下来,我们要用后台代码来实现一个简单的实例。在Program.cs文件中添加对CreateWorkflow函数的实现,代码如下:

    static Activity CreateWorkflow()
    {
        Variable<int> numberBells = new Variable<int>()
        {
            Name = "numberBells",
            Default = DateTime.Now.Hour
        };
        Variable<int> counter = new Variable<int>()
        {
            Name = "counter",
            Default = 1
        };
    
        return new Sequence()
        {
        };
    }

        在此函数中,我们首先定义了两个基于“Variable<T>”的“int”类型的工作流变量,分别叫做“numberBells”和“counter”,他们将在后续的活动中被使用。(作用第一单中已介绍)

         函数CreateWorkflow最终要求返回一个Activity类型的实例,并在WorkflowInvoker中被执行。其实通过代码不难发现,CreateWorkflow实际生成的是一个Sequance对象,这是因为Sequance是从Activity中继承而来的。(注:关于多态的说明,读者可以参考面向对象相关内容)

    第一层

        至此,我们已经定义了一个基本Sequance类型的空活动,接下来我们将对其进行充实。第一层代码如下: 

    return new Sequence()
    {
        DisplayName = "Main Sequence",
        Variables = {numberBells, counter },
        Activities = {
            new WriteLine()
            {
                DisplayName = "Hello",
                Text = "Hello, World!"
            },
            new If()
            {
                DisplayName = "Adjust for PM"
                // Code to be added here in Level 2
            },                    
            new While()
            {
                DisplayName = "Sound Bells"
                // Code to be added here in Level 2
            },
            new WriteLine()
            {
                DisplayName = "Display Time",
                Text = "The time is: " + DateTime.Now.ToString()
            },
            new If()
            {
                DisplayName = "Greeting"
                // Code to be added here in Level 2
            }
        }
    };

        在代码中,首先是设置了“DisplayName”及活动中所关联的变量,同时初始化了一系列活动,统计如下:

    活动                名称               
    WriteLine Hello
    If Adjust for PM
    While Sound Bells
    WriteLine Display Time
    If Greeting

        其中“WriteLine”活动的内容已经设置,而其他主要的活动我们将在“第二层”中提供。

     

    第二层

         为了实现第一个“If”活动,我们输入以下代码:

    DisplayName = "Adjust for PM",                        
    // Code to be added here in Level 2
    Condition = ExpressionServices.Convert<bool>
        (env => numberBells.Get(env) > 12),
    Then = new Assign<int>()
    {
        DisplayName = "Adjust Bells"
    // Code to be added here in Level 3
    }

        在代码中我们设置了“Condition”和“Then”属性,(此处并没对“Else”分支的要求)。不过对“Assign”活动的实现我们将留在下一层中介绍。

     

    表达式

        ExpressionServices的Convert<T>静态函数,可以用来创建一个“InArgument<T>”类型的临时变量,而这个类型正是“Condition”属性所需要的。同时,由于这些类和方法都是基于泛型,所以可以应用于所有类型。在本示例中我们将需要生成一个应用于“Condition”属性的“bool”类型。

        在WF环境中所有的表达式都是基于lambda表达式的。(省略一些原文中关于lambda表达式语法细节),在此处的表达式将为运行时的“Condition”进行求值。

        由于工作流实际上是状态无关的,所以它不存储任务元素的数据。这就需要通过“Variable”类型来定义一些变量来存储一些普通数据,并且可能通过其Get()方法来获取此类型的真实值。当然这都必须依赖于活动的上下文。ActivityContext。在实际应用中众多的“Variable”将在特定的工作流实例中用来区分彼此,在本处将用来判断取值是否大于12.

     

        现在,在“While”活动中输入如下代码:

     

     DisplayName = "Sound Bells",
    // Code to be added here in Level 2
     Condition = ExpressionServices.Convert<bool>
        (env => counter.Get(env) <= numberBells.Get(env)),
     Body = new Sequence()
     {
         DisplayName = "Sound Bell"
    // Code to be added here in Level 3
     }

        大家注意到其实“While”活动的“Condition”属性是依赖于一个判断值的,同样是一个通过“ExpressionService”来创建的“InArgument<T>”类型的“bool”变量。在此处将通过“count <= numberBells”来进行判断的。这时你会发现,其实两个变量都是能过其Get()方法来取得真实值的。

     

        第二个“If”活动,我们取名为“Greeting”,并输入如下代码: 

    DisplayName = "Greeting",
    // Code to be added here in Level 2
     Condition = ExpressionServices.Convert<bool>
          (env => DateTime.Now.Hour >= 18),
     Then = new WriteLine() { Text = "Good Evening" },
     Else = new WriteLine() { Text = "Good Day" }

        在代码中,会出现“Condition”中的输入参数“env”并没有真实使用,但在代码中还是被定义,并把当前时间的赋值给它,用来判断其值是否已过下午6点。在“Then”和“Else”属性中,分别创建了一个“WriteLine”活动,用来输出“Good Evening”和“Good Day”。

     

    第三层

         在第一个“If”活动(那个叫“Adjust for PM”的活动)中,我们创建了一个空的“Assign”活动在它的“Then”属性中。为了完善它,我们输入如下代码: 

     DisplayName = "Adjust Bells",
    // Code to be added here in Level 3
     To = new OutArgument<int>(numberBells),
     Value = new InArgument<int>(env => numberBells.Get(env) - 12)

     

    Assign活动

        “Assign”类型是支持泛型的。在此处我们要操作的是一个整型值,所以可以定义为“Assign<int>”。在本活动的“TO”属性是一个“OutArgument”类型,这个将通过原先定定义好的一个变量来构造,而“Value”属性则使用了一个“InArgument”类型。本活动操作的结果,将在后继的“If”和“While”活动的“Condition”属性中用,所以必须在提前完成。

     

    Sequence

         在“While”活动中,我们为“Body”提供的是一个空“Sequence”。它定义的一系列活动将在循环体中不断的被执行。代码如下:

     

     DisplayName = "Sound Bells",
    // Code to be added here in Level 2
     Condition = ExpressionServices.Convert<bool>
         (env => counter.Get(env) <= numberBells.Get(env)),
     Body = new Sequence()
     {
         DisplayName = "Sound Bell",
         // Code to be added here in Level 3
         Activities =
         {
             new WriteLine()
             {
                 Text = new InArgument<string>(env => counter.Get(env).ToString())
             },
             new Assign<int>()
             {
                 DisplayName = "Increment Counter",
                 To = new OutArgument<int>(counter),
                 Value = new InArgument<int>(env => counter.Get(env) + 1)
             },
             new Delay()
             {
                 Duration = TimeSpan.FromSeconds(1)
             }
         }
     }

        以上代码为“Sequcence”添加了三个活动:

            一个用于显示“counter”值的“WriteLine”活动。

            一个用于增加“counter”值的“Assign”活动。

            一个用于延时的“Delay”活动。

     

         在本例中,“WriteLine”活动并不是每次都输出统一的文本,而是显示一个表达式的值。由于它的“Text”属性需要的是一个“string”类型,所以我们定义了一个“InArgument<string>”类型来提供支持。现在,我们可以使用这些lambda表达了,通过其Get()来获取变量的当前值,同时通过“ToString”把“int”转换成了“string”。

        当然在“Delay”活动中,我们是通过“TimeSpan”类型的“FromSeconds”静态方法来实现延时效果的。

     

    运行程序(F5

     

    源代码:Chapter02

     

    与本系列相关的所有文档及代码索引请参考:

    《Beginning WF : Windows Workflow in .NET 4.0》读书笔记

  • 相关阅读:
    浙大《数据结构》第二章:线性结构
    浙大《数据结构》第一章:基本概念
    《软技能:代码之外的生存指南》读书笔记
    《高质量程序设计指南》读书笔记
    《大话无线通信》读书笔记
    使用Tensorflow训练神经网络模型
    掌握功率谱估计的方法
    网络安全宣传周活动
    ICMP数据包
    DNS数据包
  • 原文地址:https://www.cnblogs.com/showjan/p/3008338.html
Copyright © 2020-2023  润新知