WF(Windows Workflow Foundation,Windows工作流基础)为.NET提供了一种基于模型的、声明方式的过程执行引擎,它改变了传统的通过一行行编写代码来开发服务功能的方式。

  WF包含三个核心组件:活动框架(activity framework)、运行时环境(runtime environment)、工作流设计器(workflow designer)。

WF不是什么

  工作流这个词在软件开发领域和相关社区里已经被“滥用”了。所以弄清楚WF在这些流行的工作流概念中到底指的是哪一种就非常重要。

  1. WF不是服务器,虽然可以将工作流功能集中起来然后通过服务器暴露给其他应用程序使用。

  2. WF不是一种BPM(Business Process Management,业务流程管理)工具,虽然可以将WF作为工作流执行引擎来创建BPM工具。

  3. WCF并不直接面向业务分析人员,虽然可以使用可重新承载的设计器向业务分析人员提供创建自己的工作流的工功能。WF非常灵活,它允许将此功能集成到分析人员熟悉的环境中。如果现有的设计器不能满足要求,还可以创建定制的工作流设计器。

  4. WF不是企业应用集成工具,虽然可以将第三方的系统功能封装在活动中,然后将它们加到工作流里。

  5. WF不是一个玩具。这可以在具有大量服务器集群的企业级规模环境里高性能地运作。它可以胜任企业级应用,SharePoint Server就是一个很好的应用实例。而WF本身并不是一个企业级应用,它只是一组开发套件。

  WF不仅可以用在基于服务器的应用程序里,它还可以用在WinForm应用中执行从服务协调到用户界面定制的任何应用程序逻辑。也可以用在Web应用程序中以管理流程状态。总之,任何可以编写.NET代码的地方都可使用它作为提供逻辑的代码。

活动

  活动(activity)是WF的基本构建模块。从复杂的执行逻辑到执行一个对SQL数据库的更新,这些操作都被封装到一个个独立的工作单元,也就是活动中。活动是所有直接或间接继承自System.Workflow.ComponentModel.Activity的类。活动具有两个方面的行为:运行时行为(runtime behavior)和设计时行为(design-time behavior)。

  运行时行为是活动在工作流中使用时执行的代码。它可能是访问一个Web服务或执行一块代码,也可能是协调子活动的执行。一个常被问及的问题是:WF的性能怎样?在活动级别,答案很简单:活动可以和在.NET程序集里相同代码的执行速度一样快。因为活动只是一个已经编译好的.NET程序集,它包含从activity继承的类。管理活动的生命周期需要额外的开支。

  当包含活动的工作流被创建时,该活动就会被初始化并停留在Initialized(初始)状态。也就是说,当CreateInstance方法被调用且数据流实例被创建时,所有的活动都将被初始化。服务在被调度执行时,其状态会变成Executing(执行)。如果一切正常,活动会进入到Closed(关闭)状态,也就是它的任务已完成,可以休息了。

  活动可能会遇到错误,从而进入Faulting状态而被关闭。活动也可能被其他的活动取消掉,从而进入Canceling状态,然后再进入Closed状态。最后,如果活动为其执行定义了回滚或补偿操作,活动就会从Closed状态激活并进入到Compensating状态。由于某些原因,一个错误或一个取消处理都需要调用补偿逻辑。

  另外,活动的设计体验对创建工作流是非常重要的。活动可能需要在设计窗口里有些个性化的表示,目的是为了告诉开发者可以一目了然地知道活动正在做什么。

开箱即用活动

  随WF一起发布的活动通常被称为开箱即用活动(out of the box activity)。它们是一组基本的活动,可用来创建简单的工作流和组建新的活动。下表给出了每个开箱即用活动的简单介绍:

创建定制服务

  工作流开发者可能经常需要创建定制活动。从封装经常使用的方法到创建能为一种新的执行模式建模的复合活动,开发者在开始一个工作流项目时需要首先思考他需要什么样的活动。随着时间的推移,这些活动可被复用或被用来组建更高层次的活动,最后就像处理现在的普通对象一样。

基础

  创建定制活动的最基本方法就是从System.Workflow.ComponentModel.Activity继承一个类。这将为活动创建所有的基本机制,除了活动实现的实际逻辑。可通过重载Execute方法来实现实际的逻辑。如下所示:

public class BasicActivity : Activity
{
public BasicActivity()
{
this.Name = "BasicActivity";
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
Console.WriteLine("Basic Activity");
return ActivityExecutionStatus.Closed;
}
}

  Execute方法执行活动的操作并需同时通知运行时它的状态。在这里,它返回Closed状态,表示该活动已完成它的工作。一个更加复杂的模式是:返回Executing状态,同时等待一个需要运行较长时间的工作完成。当这个较长时间的工作完成时,活动将通知运行时它已经完成了。在等待时,工作流实例可能停顿下来较长时间。

  我们经常需要控制一些与活动相关的参数,它们会影响活动所提供的有用功能。最简单的方法就是为活动类添加属性,如下所示:

public string TextToPrint
{
get { return textToPrint; }
set { textToPring = "value"; }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
Console.WriteLine("Text to print: {0}", TextToPrint);
return ActivityExecutionStatus.Closed;
}

  当使用XAML声明式地创建工作流时,这些属性会被作为活动的特性(attribute),如下所示:

<SequentialWorkflowActivity x:Class="SampleWFApplication.Workflow1"
x:Name="Workflow1"
xmlns:ns0="clr-namespace:SampleWFApplication"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
<ns0:BasicActivity TextToPrint="Hello World"
x:Name="basicActivity1" />
</SequentialWorkflowActivity>

  这样就允许在设计器里设定属性的值从而定制活动的行为。但这还是静态的,属性被限制为设计时设定的值。当创建的工作流需要评估传入的定制对象时呢?这可以通过使用依赖属性(dependency property)来实现。依赖属性和之前介绍的.NET标准属性类似,但在声明和用户法上有所不同。VS中有内建的代码来创建它们,如下所示:

public static DependencyProperty OrderAmountProperty = System.Workflow.ComponentModel.
DependencyProperty.Register("OrderAmount", typeof(int), typeof(BasicActivity));
[Description("This property holds the amount of the order")]
[Category("Order Details")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int OrderAmount
{
get
{
return ((int)(base.GetValue(BasicActivity.OrderAmountProperty)));
}
set
{
base.SetValue(BasicActivity.OrderAmountProperty, value);
}
}

  这个稍长的声明里不仅有属性的声明,而且还包含了一个静态DependencyProperty变量的声明。DependencyProperty是一种添加到DependencyObject(Activity就继承自DependencyObject)的特殊属性类型。DependencyProperty不同于传统属性,它可以支持三种特殊的使用方式:

  1. 活动绑定。

  2. 元数据,只在设计时赋值,在运行时不能更改。

  3. 附加属性(attached property),动态地为活动添加属性。

  使用依赖属性的最普通场景是支持活动绑定。使用依赖属性的好处是可以添加新的设计时行为。将活动拖到设计窗口后,观察它的属性窗口,会发现在刚长声明的属性旁边新增一个new图标。单击该图标会弹出一个新的Bind(绑定)对话框。

  Bind对话框允许将属性的值与工作流中的另外一个值动态绑定。这个值可以是在工作流创建时传入该工作流的一个属性,也可以是另外一个活动的属性。在设计时,活动被告知到哪儿去查找依赖属性的值。通过选择工作流中同一类型(可以是一个定制类型)的另外一个值,一个绑定表达式会出现在属性窗口里作为这个依赖属性的值。表达式与下面的式子类似的:

  Activity=Workflow1, Path=WorkflowOrderAmount

  式子的第一部分表示源(这里是父工作流),然后是活动可以使用的属性。式子里可以使用点操作符,如果希望被绑定的值是属性的下面几层,它也可以被访问。依赖属性的值还可以直接写在代码里。

  Bind对话框里有一个Bind to a New Member(绑定到一个新成员)标签页。这个窗口可以使一个活动的依赖属性“提升”(promote)为窗口活动(即包含活动的活动)的一个成员。

组合

  第二种创建活动的方法是复合(composition),这也是VS的默认方法。以下代码在VS中新建一个活动类的定义:

public partial class ACompositeActivity : SequenceActivity

  它继承自SequenceActivity。SequenceAcitivity是创建顺序执行工作流的基类,它的Execute方法负责将包含的活动依次调度执行。通过继承,可保留我们希望的这种模式。这将使活动的开发者通过拖拽其他活动到新的活动里来创建一个新的活动,也就是从现有的活动创建新的活动。这是一个创建新工作单元的强大工具。这意味着,如果有一组已经实现且足够强大的基本活动(如对现有API的封装)的集合,就可使用它们快速创建更高层次的新功能单元。

  考虑下面的活动:

  1. SendEmail

  2. LookupManager

  3. WaitForResponseToEmail

  使用这些活动及一些开箱即用活动提供的结构化活动,可以创建任意复杂的标准活动,然后新建的活动就可以作为批准活动被任意的工作流使用。

定制复合活动

  我们还可以创建另外一类活动,即定制复合活动(custom composite activity)。复合活动包含子活动,它的Execute方法负责调度执行这些子活动。在开箱即用活动中,Sequence、While和Parallel都是复合活动的例子。

  复合活动执行时,会执行它的子活动并订阅这些子活动的完成事件。活动会返回Executing状态,向运行时指明它要继续执行下一个被调度的活动。通过接收子活动的完成事件,复合活动可以继续调度执行其他的活动或判断是否可以结束。当所有的活动都结束了或复合活动决定结束时,它会返回Closed状态,表明它已经结束了。工作流会确保在允许一个活动关闭前,它的所有子活动都关闭了。

活动通信

  工作流并不是在完全孤立的环境里执行的,它们经常需要和宿主程序交互以发送消息给宿主或从宿主接收消息。两个开箱即用活动被设计成支持这样的交互:Handle-ExtenalEvent和CallExternalMethod。这些活动通过宿主和工作流间共享的契约通信。该契约的实现作为本地服务提供给运行时ExternalDataExchangeService。

  让我们来看一个基于员工调查应用场景的例子。首先创建一个在宿主和工作流间共享的契约,并为其添加ExtenalDataChange特性:

using System;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;

namespace ExternalEventSample
{
[ExternalDataExchange()]
public interface ISurveyResponseService
{
void SurveyEmployee(string employee, string surveyQuestion);
event EventHandler<SurveyEventArgs> SurveyCompleted;
}

[Serializable]
public class SurveyEventArgs : ExternalDataEventArgs
{
private string employee;
public string Employee
{
get { return employee; }
set { employee = value; }
}
private string surveyResponse;
public string SurveyResponse
{
get { return surveyResponse; }
set { surveyResponse = value; }
}

public SurveyEventArgs(Guid instanceId, string employee, string surveyResponse) : base(instanceId)
{
this.employee = employee;
this.surveyResponse = surveyResponse;
}
}
}

  该接口定义了一个事件、一个定制的事件参数类和一个公共方法。我们再提供一个接口的实现:

using System;

namespace ExternalEventSample
{
class SurveyResponseService : ISurveyResponseService
{
public void SurveyEmployee(string employee, string surveyQuestion)
{
//here we would notify and display the survey
Console.WriteLine("Hey {0}, what do you think of {1}?", employee, surveyQuestion);
}
public event EventHandler<SurveyEventArgs> SurveyCompleted;
public void CompleteSurvey(Guid instanceId, string employee, string surveyResponse)
{
// the host will call this method when it wants to raisethe event into the workflow.
// Note that the workflow instance id needs to be passed in.
EventHandler<SurveyEventArgs> surveyCompleted = this.SurveyCompleted;
if(surveyCompleted != null)
{
surveyCompleted(null, new SurveyEventArgs(instanceId, employee, surveyResponse));
}
}
}
}

  现在,将ExternalDataExchange服务添加到运行时,并且将接口实现的一个实例添加为本地服务。使用OnWorkflowIdled事件发送响应给宿主。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;

namespace ExternalEventSample
{
class Program
{
static SurveyResponseService surveyService;
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
// add the local service to the external data exchange service
surveyService = new SurveyResponseService();
ExternalDataExchangeService dataService = new ExternalDataExchangeService();
workflowRuntime.AddService(dataService);
dataService.AddService(surveyService);
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{ waitHandle.Set(); };
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
workflowRuntime.WorkflowIdled += OnWorkflowIdled;
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof
WorkflowConsoleApplication13.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
}
static void OnWorkflowIdled(object sender, WorkflowEventArgs e)
{
surveyService.CompleteSurvey(e.WorkflowInstance.InstanceId, "Matt", "Very Statisfied");
}
}
}

  回到工作流,将CallExternalMethod活动拖到设计窗口。注意智能标签验证表明接口类型和方法名称都没有定义。点击InterfaceType属性会弹出一个标准的类型浏览窗口以选择合适的接口。方法可以从MethodName属性的下拉列表中选择,方法选择好后,对应于输入参数和接口里定义的输出的其他属性就会添加到属性窗口里。

  当CallExternalMethod活动执行时,它通过ActivityExecutionContext.GetService获得对契约实现的访问,并调用其方法。

  为使用HandleExternalEvent活动,要做的第一件事就是为宿主程序提供一种为工作流运行时触发事件的方法,以接收和路由消息。在上例中通过调用服务实现的方法CompleteSurvey方法来完成。该方法将触发一个事件,运行时会根据传入的workflowId参数将其路由给相应的工作流实例。在内部实现时,HandleExternalEvent会创建一个队列并订阅放在队列里的消息。当接收到一个事件时,运行时会将消息放在正在等待该类型消息的队列上。可以让多个队列同时监听同一种类型消息--如等待多个员工完成调查。在这种情况下,事件将被路由到哪个队列的粒度可通过使用correlation来指定。

  为使用HandleExternalEvent,首先将它拖到设计窗口里,其过程与CallExternalMethod类似。当工作流到达HandleExternalEvent活动时,它会创建一个队列来监听指定类型的事件类型,然后,或者停下来,或者继续执行其他被调度执行的活动。

转载于:https://www.cnblogs.com/free722/archive/2011/05/23/2053580.html

WCF揭秘学习笔记(5):WF定制活动相关推荐

  1. MonoRail学习笔记五:定制服务实现自定义功能

    在上一篇MonoRail学习笔记四:MonoRail基本流程分析 中我提到,MonoRail中可以自定义一些服务.比如可以定义自己的Url解析类,来实现http://localhost:***/ind ...

  2. 第一行代码学习笔记第二章——探究活动

    知识点目录 2.1 活动是什么 2.2 活动的基本用法 2.2.1 手动创建活动 2.2.2 创建和加载布局 2.2.3 在AndroidManifest文件中注册 2.2.4 在活动中使用Toast ...

  3. Programming WCF Services 学习笔记四、Instance Management

        1.         Behaviors i.              Service的实例模型是客户端无关的,即Client端不会知道Service的实例模型,Service的实例模型也不 ...

  4. WCF(学习笔记)【参见WCF教程】

    WCF:基于SOA(面向服务编程 )的一个.net框架产品   一种分布式通讯的统一模型(分布式通讯就是对多种 操作系统和平台的的兼容) 通信范围比较广 Web service             ...

  5. Activiti 学习笔记十:开始活动节点(判断流程是否结束及查询历史)

    一.流程图 二.演示代码 package jiankunking.start;import java.io.InputStream;import org.activiti.engine.Process ...

  6. 网络扫描技术揭秘学习笔记《二》NetBIOS/NrtBEUI协议编程

    <二>NetBIOS/NrtBEUI协议编程 1. NetBIOS简介 NetBIOS协议既可以是一个面向连接的数据包服务,也可以是面向非连接的对话服务.早起的NetBIOS只适用于局域网 ...

  7. 方舟编译器学习笔记分类与导读

    方舟学习笔记系列,从方舟开源到现在,已经写了50多篇,保证了每天一篇的更新频率.篇数增加之后,文章的分类以及文章之间的关系,逐渐变得复杂起来.本文将对已发表的学习笔记系列进行分类和导读,方便读者更好的 ...

  8. QT学习笔记(摘抄)

    QT学习笔记-1.QT主要的对象 说来惭愧学习c++很长时间了一直没有使用c++开发过软件界面 所以现在想认认真真的学习一个c++图形界面框架库 本来想学习Xwidget但是这个资料不大好找 有啥问题 ...

  9. WCF服务编程 学习笔记(1)

    你或许可以使用某一技术实现某些功能,可以按着指定的要求,完成特定的功能,实现某一想要的效果,这表示你可以使用该技术,会使用该技术,但是我们不能停留在使用的层次上,还要了解它们的运行机制,可能有点深了, ...

最新文章

  1. Loadrunner11如何使用非IE浏览器录制脚本
  2. 教你一步步发布一个开源库到 JCenter
  3. SQLite 运算符(http://www.w3cschool.cc/sqlite/sqlite-operators.html)
  4. java对象序列化去掉字段_使用序列化查找对象中的脏字段
  5. 【[USACO09DEC]牛收费路径Cow Toll Paths】
  6. hyperion卫星重访时间_观摩卫星发射|2020第四届全球物联网大会上让我们一起去“放星”...
  7. QQ号终于可以当传家宝了!没车没房的,就留几个游戏账号给儿子吧?
  8. 异步promise、Async/await介绍
  9. 【光学】Matlab实现色散曲线拟合
  10. 如何设置sap生产订单自动关闭
  11. 百度图像识别明星或动物
  12. 关于符号Symbol第二篇
  13. 经过这一篇解决Mysql的大多数基础问题
  14. 静态代理的实现-模拟中介代理房东出租房子给房客
  15. 聊天气泡图片的动态拉伸、适配与镜像
  16. 一个IP可以登几个拼多多后台 拼多多如何推广营销
  17. ampserv mysql升级_APMServ5.2.6升级PHP
  18. 用html+css做一个网页设计
  19. element-ui dialog遮罩层在最上层,关掉dialog遮罩层还在
  20. spark运行出现py4j.protocol.Py4JError异常

热门文章

  1. OpenGL环境下的射线选择
  2. 函数计算 【题目描述】
  3. 如果你有15M 你会投到那些项目上上面呢?机会-可行性模型帮助你
  4. 逆向工程(Reverse Engineering)
  5. matlab kill
  6. Python数据类型(列表和元组)
  7. 干货分享:六个知名的Go语言web框架
  8. 学习笔记之与 30 家公司过招,得到了这章面试心法
  9. Centos7安装防火墙firewall
  10. AssemblyExecuteAdapter