UML软件工程组织

 

 

Windows Workflow Foundation之旅(三)
 
作者:wolf's cave 文章来源:CSDN
 

Windows Workflow Foundation之旅(五)——指南3(创建自定义活动)

翻译自: ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/8b6344bc-c879-41c5-babf-74e91c34d281.htm

WWF包含了一组丰富的通用用户活动,这些能满足绝大多数的场景的需求。但有时,我们还是会碰到需要一些更加自定义的活动的场景。WWF SDK拥有一些可扩展的特性,能使你轻松的创建自定义的活动,并把他们应用到你的解决方案中。在这一节中,我们会编写一个自定义活动,用来根据传入工作流的参数,从网页中下载文本。

创建一个网页解析自定义活动

这个自定义活动会根据传入活动的网页属性集,从网页中下载文本。页面一旦下载完毕,活动就会发出一个网页下载完毕的事件,并把网页数据值发回工作流。

创建WebTearActivity类

WebTear类继承自System.Workflow.ComponentModel.Activity类。当你定义了一个自定义活动时,把ToolboxItemAttributes属性应用到类上,并指明为ActivityboxItem类型。以下的代码就是一个最小的自定义活动。

using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
 
namespace Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{
    [ToolboxItemAttribute(typeof(ActivityToolboxItem))]
    public class WebTear : System.Workflow.ComponentModel.Activity
    
{
    }

}


 定义活动属性

自定义活动属性的定义和WinForm控件属性的定义类似。当你定义一个自定义活动的属性时,使用DependencyProperty来存储属性的属性。这能让各种不同类型的属性都能一致地有效地工作。在实际定义属性时,你可能还会用到DependencyProperty类中的方法,所以,不要把这个字段定义成private。

活动中的一个属性可以应用许多不同的特性。这些特性通常都是用来支持WWF设计器的工作的。我们的这个属性应用了一下的特性:

1. CategoryAttribute:在设计器的属性窗口中,这个属性位于那个分类下。

2. DescriptionAttribute:设计器属性窗口中显示的属性描述。

3. BrowsableAttribute:这是一个布尔值,指示了是否在属性窗口中显示这个属性。

4. ValidationVisibilityAttribute:指定了如何验证属性的值。验证选项包括:可选Optional、必需Required、隐藏Hidden。

5. DesignerSerializationVisibilityAttribute:指定了是否和如何序列化这个属性。选项包括:可见Visible、隐藏Hidden、内容Content。

以下的代码示例了如何定义WebPage属性。

public static DependencyProperty WebPageProperty =
    DependencyProperty.Register("WebPage", typeof(System.String),
 typeof (Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear ) );
 
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationVisibilityAttribute(ValidationVisibility.Optional)]
[BrowsableAttribute(true)]
[DescriptionAttribute("Web page to download")]
[CategoryAttribute("WebTear Property")]
public string WebPage
{
    get

    {
        return ((string)(base
.GetValue(Microsoft.Samples.Workflow.
Quickstarts.CustomActivity.WebTear.WebPageProperty)));
    }

    set
    {
        base
.SetValue(Microsoft.Samples.Workflow.Quickstarts.
CustomActivity.WebTear.WebPageProperty, value);
    }

}

 

重载Execute方法

当你创建了一个自定义活动,运行时,工作流引擎会调用活动的Execute方法来执行活动实际的操作。这个方法是在基类中定义的,但你可以重载这个方法,以适应你的活动的功能。

WebTear活动会在Execute方法中下载请求的网页,并在下载完成后引发一个事件,将页面数据发回工作流。我们需要定义一个PageFinishedEventArgs类用于事件参数,这个类中有一个Data字段用来存放网页数据。这些数据会在之后被工作流中的事件处理函数访问到。一旦数据下载完毕,Execute方法返回一个Status.Closed枚举值,来通知工作流引擎活动已经完成执行了。

一下的代码演示了如何定义PageFinishedEventArgs类

public class PageFinishedEventArgs
{
    private string data;
 
    public PageFinishedEventArgs( string data )
    
{
        this.data =
 data;
    }

 
    
public string Data
    
{
        get { return data; }

    }
}

 

以下的代码演示了如何实现Execute方法。
 

public delegate void PageFinishedEventHandler( object sender, PageFinishedEventArgs e );
public event PageFinishedEventHandler PageFinished;
 
protected override Status Execute(ActivityExecutionContext context)
{
    System.Net.WebClient client = new System.Net.WebClient();
    string pageData;
 
    try

    {
        // Download the web page data

        pageData = client.DownloadString(WebPage);
    }

    catch (Exception e)
    
{
        pageData =
 e.Message;
    }

 
    
// Raise the PageFinished event back to the host
    PageFinished(null, new PageFinishedEventArgs(pageData));
 
    // Notifiy the runtime that the activity has finished

    return Status.Closed;
}

 

为WebTear活动创建顺序工作流。

此时,我们已经创建好了下载网页的自定义活动。现在我们创建一个简单的顺序工作流,来测试一下这个自定义活动的功能。

这个工作流只包含一个活动,就是我们的WebTear活动。要下载的网页地址被传到工作流的Parameters集合中,这个的值用来设置活动的WebPage属性。最后,当活动结束下载后,触发事件,工作流会从事件参数中提取网页数据,并把数据通过Parameters集合中的out参数发回宿主程序。以下代码演示了顺序工作流的实现


 

using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities;
 
namespace Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{
    public sealed partial class WebTearActivityWorkflow : SequentialWorkflow
    
{
        private string webSite;
        private WebTear webTear1;
 
        public WebTearActivityWorkflow()
        
{
            InitializeComponent();
        }

 
        
private void InitializeComponent()
        
{
            System.Workflow.ComponentModel.ActivityBind activitybind1 =
 new System.Workflow.ComponentModel.ActivityBind();
            System.Workflow.ComponentModel.ParameterDeclaration WebPage = 
new System.Workflow.ComponentModel.ParameterDeclaration();
            System.Workflow.ComponentModel.ParameterDeclaration PageData =
 new System.Workflow.ComponentModel.ParameterDeclaration();
            this.webTear1 = new Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear();
            // 
            // webTear1
            // 

            this.webTear1.ID = "webTear1";
            activitybind1.ID = "/Workflow";
            activitybind1.Path = "webSite";
            this.webTear1.PageFinished += new Microsoft.Samples.Workflow.Quickstarts.
CustomActivity.WebTear.PageFinishedEventHandler(this.webTear1_PageFinished);
            this.webTear1.SetBinding(Microsoft.Samples.Workflow.Quickstarts.
CustomActivity.WebTear.WebPageProperty, ((System.Workflow.ComponentModel.Bind)(activitybind1)));
            // 
            // WebTearActivityWorkflow
            // 

            this.Activities.Add(this.webTear1);
            this.DynamicUpdateCondition = null;
            this.ID = "WebTearActivityWorkflow";
            WebPage.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
            WebPage.Name = "WebPage";
            WebPage.Type = typeof(System.String);
            WebPage.Value = null;
            PageData.Direction = System.Workflow.ComponentModel.ParameterDirection.Out;
            PageData.Name = "PageData";
            PageData.Type = typeof(System.String);
            PageData.Value = null;
            this.Parameters.Add(WebPage);
            this.Parameters.Add(PageData);
            this.Initialized += new System.EventHandler(this
.InitVars);
 
        }

 
        
private void InitVars(object sender, EventArgs e)
        
{
            webSite = this.Parameters["WebPage"
].Value.ToString();
        }

 
        
private void webTear1_PageFinished(object sender, PageFinishedEventArgs e)
        
{
            this.Parameters["PageData"].Value =
 e.Data;
        }

    }
}


 创建宿主程序

这里的宿主程序是一个Winform程序,它有一个TextBox来输入要下载的url,当点击Go按钮,工作流开始运行,并执行WebTear活动开始下载网页。工作流运行完后,宿主程序从WorkflowCompleteEventArgs对象中获得网页数据,并把它显示在另一个TextBox中。以下代码演示了如何实现宿主程序(译者注:私自去掉了不重要的代码,要看完整的自个儿去msdn上找,呵呵)。


 

……

namespace Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{
    public class MainForm : Form
    
{
        private System.Windows.Forms.Label addressCaption;
        private System.Windows.Forms.TextBox address;
        private System.Windows.Forms.TextBox data;
        private System.Windows.Forms.Button goButton;
 
        private WorkflowRuntime workflowRuntime;
 
        public MainForm()
        
{
            InitializeComponent();
 
            workflowRuntime = new WorkflowRuntime();
            workflowRuntime.StartRuntime();
 
            workflowRuntime.WorkflowCompleted += new EventHandler
<WorkflowCompletedEventArgs>
(workflowRuntime_WorkflowCompleted);
        }

 
        
void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
        
{
            // Retrieve the downloaded page data

            if (data.InvokeRequired)
                data.Invoke(new EventHandler<WorkflowCompletedEventArgs>
(workflowRuntime_WorkflowCompleted), sender, e);
            else

                data.Text = e.OutputParameters["PageData"].ToString();
        }

 
        
private void goButton_Click(object sender, EventArgs e)
        
{
            Type type = typeof(Microsoft.Samples.Workflow.Quickstarts.
CustomActivity.WebTearActivityWorkflow);
 
            // Sending the data to the workflow.
            // First create the required property set 

            Dictionary<string, object> properties = new Dictionary<string, object>();
            properties.Add("WebPage", address.Text);
            properties.Add("PageData", ""
);
 
            workflowRuntime.StartWorkflow(type, properties);
        }

 
        ……
    }

}


 Windows Workflow Foundation之旅(六)——框架组成

翻译自:ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/68db11eb-3d20-41ca-90f7-ae635fd4e2a2.htm

这个专题前面的三个指南,介绍了WWF编程了三个大方面:顺序工作流、状态机工作流和自定义活动。相信大家对WWF的编程模型已经有了一个初步的了解。从这次开始,我们就要深入WWF,全面的探究一下WWF。

传统的编程语言是针对短期运行应用程序的,缺乏持久化和抗中断的功能支持。WWF是一个框架,不是一种语言。他对工作流应用程序有深刻的认识,提供了各种手段来处理持久化,中断补偿,故障恢复等功能。

WWF同时具有灵活性和可扩展性。你可以直接用代码定义工作流,也可以用外部的定义语言也定义,或同时使用两者。你可以用你自己的自定义活动来实现自定义的工作流模式,以到达可重用的目的。

WWF支持模型驱动的开发,提供了可视化的设计工具,并隐藏了一些系统级的功能,如事务、状态管理和并发控制,是开发者可以专注于业务模型。

WWF中的工作流和活动

工作流是一组相关的活动的集合。

活动是WWF中可建模、可编程、可重用、可执行的原子单位。WWF提供了一组丰富的活动库,来构建你的工作流。

WWF的工作流应用程序

一个应用程序必须做以下步骤,才能使用WWF:

1. 创建一个WorkflowRuntime类的实例。它代表了工作流引擎。

2. 为运行时引擎配置服务。

3. 启动引擎。

4. 使用引擎来创建和管理工作流。

当工作流运行时, 引擎使用大量的可插拔的服务来处理持久化、事务、线程、跟踪和计时器等任务。

你可以通过配置这些服务来扩展和修改运行时引擎的行为,也可以创建自定义的服务来提供另外的功能。

(译者)很多朋友在回复中提了几个方面的疑问。这里我统一解答下:

  • WWF支持以编程方式,固化的定义工作流,也支持以XML文件形式定义工作流。
  • WWF可以以编码的方式来创建工作流,也可以用工作流模型设计工具来定义,这些工具集成在了vs2005中。我会在以后的随笔中介绍设计工具。用设计工具创作的工作流也可以导出为XML定义。
  • WWF目前也处于测试阶段,不太适合于商用。
  • WWF目前版本仅支持.NET Framework 2.0。而vs2005又不支持1.1,所以,vs.net2003下不能开发WWF应用。

下面列出的是你在创建工作流中会用到的一些概念。

  • 顺序工作流
  • 状态机工作流
  • 工作流创作模式
  • 使用活动控制流程
  • 在工作流中使用条件
  • 在工作流中使用事务
  • 获得工作流中的状态信息
  • 在工作流中使用异常
  • 在工作流中使用补偿?(Compensation)
  • 使用时间处理活动
  • 在工作流中使用代码
  • 在工作流中使用动态更新
  • 在工作流中使用数据
  • 在工作流中使用角色
  • 在工作流中使用XML支持
  • 编译工作流
  • 序列化工作流

之后的篇章中,我讲详细解释以上的概念。

工作流创作模式

ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/aacf4ec6-da05-4974-958a-974769dda739.htm

WWF支持两种不用的工作流创作风格:顺序工作流和状态机工作流。顺序工作流只向前运行的,它也用于那些重复的,流程相对固定的场景中。状态机工作流讲工作流设计为一个状态机。工作流本身包含了一组状态。

默认情况下,WWF将工作流定义与业务逻辑分隔开。在一个典型的创作场景中,设计着使用设计器来设计工作流定义,然后使用C#或其他.NET语言也编写业务逻辑。这种创作模式叫代码分隔(code-separation)。

WWF支持以下创作模型:

  • Code-only 这是WWF默认的创作模式。在这种模式下,你需要使用WWF的API,以编程的方式用代码来定义工作流。工作流定义代码和业务逻辑代码将混合在一起。
  • Code-separation 这种模式可以同时使用设计器和代码。
  • No-code 这种方式下,你可以使用任何的文本编辑器来定义工作流,然后使用WWF命令行编译器来编译文本工作流定义。

想了解更多工作流创作模式的信息,请看 顺序工作流,状态机工作流,在工作流中使用代码,编译工作流(没有链接的章节在将写好后加上链接)。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号