VS2010 学习笔记 WF4 (6) 使用WorkflowApplication启动工作流

到目前我们知道如何创建一个工作流节点(activity)和使用最简单的方法来启动工作流,就是使用WorkflowInvoker 类。WorkflowInvoker.Invoke 方法比较简单,它属于同步调用,工作流启动的线程与调用程序的线程是一样的。

 

启动工作流另外一个方法是使用WorkflowApplication 类,相对WorkflowInvoke类,它可以允许工作流在另外一个独立线程运行,提供了当工作流结束的时候可以调用的delegates。比起不使用WF4,可以更加简单和快捷的创建多线程服务器或客户端程序。

 

这节会学习到,修改启动程序用WorkflowApplication来运行SayHello节点,并观察线程表现。要求可以达到以下功能:

  1. 返回一个个性化的问候语
  2. 返回一个非零的Int32数值,代表工作流运行的managed thread ID

我们还是使用TDD的先写测试方法的做法,看看如何完成以上的要求。

 

任务1 – 编写测试,验证可以将工作流线程ID作为输出参数返回

 

  • 在HelloWorkflow.Tests项目下,打开SayHelloFixture.cs,并添加以下命名空间:

using System.Threading;
using System.Diagnostics;

  • 添加ShouldReturnWorkflowThread 测试方法,代码如下:

/// <summary>
/// Verifies that the workflow returns an Out Argument
/// Name: WorkflowThread
/// Type: Int32
/// Value: Non-Zero
/// </summary>
[TestMethod]
public void ShouldReturnWorkflowThread ()
{
    var output = WorkflowInvoker.Invoke(
     new SayHello()
     {
         UserName = "Test"
     });

    Assert.IsTrue(output.ContainsKey("WorkflowThread "),
        "SayHello must contain an OutArgument named WorkflowThread");

    // Don't know for sure what it is yet
    var outarg = output["WorkflowThread"];

    Assert.IsInstanceOfType(outarg, typeof(Int32) ,
        "WorkflowThread must be of type Int32");

    Assert.AreNotEqual (0, outarg,
        "WorkflowThread must not be zero");

    Debug.WriteLine("Test thread is " +
                    Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine("Workflow thread is " + outarg.ToString());
}

  • 上述的代码测试了
    • output字典里存在WorkflowThread输出参数
    • 该参数值是一个Int32类型
    • 该参数值不是0
  • CTRL+R, A 运行所有测试。可以预料ShouldReturnWorkflowThread 将会失败,因为我们还没有添加WorkflowThread这个输出参数

任务 2 – 添加WorkflowThread输出参数

 

写好了测试方法后,要做的就是去实现功能使测试能够通过。

  • 打开SayHello.xaml 并添加新的参数
    • 参数WorkflowThread,方向Out ,类型Int32

到现在为止,SayHello只有一个Assign节点。我们需要2个Assign节点,一个是用于给Greeting赋值,另外一个是给WorkflowThread赋值。因此要用一个可以放置多个节点的容器节点来存放这两个Assign节点。可以有很多容器节点选择,但最简的是使用Sequence 节点。

  • 在删除已有的Assign节点前,是不能直接拖动Sequence节点到设计器界面的。由于现在这个Assign需要保留,所以我们可以使用剪切的方式清除此节点。右键点击Assign节点,并选择Cut
  • 从工具栏拖动Sequence 并放置于设计界面
  • 在Sequence节点内部,右键点击并选择Paste 将之前复制好的Assign节点放到里面
  • 由于要获取线程ID,所以要输入(ImportSystem.Threading 命名空间。在设计器下方,选择Imports 并添加System.Threading ,通过输入头几个字符来列出要添加的命名空间,找到需要的名称后按回车
    • 不添加Imports,可以使用线程类方法,但必须在类的前面加上System.Threading ,例如System.Threading.Thread
  • 从工具栏拖动Assign 节点到Sequence内,并完成对WorkflowThread的赋值
    • To :WorkflowThread
    • Value :Thread.CurrentThread.ManagedThreadID
  • CTRL+SHIFT+B 编译
  • CTRL+R,A 运行所有测试,应该全部能够通过。要查看Debug.Print输出结果,可以在Test Results窗口,双击ShouldReturnWorkflowThread ,会出现Debug输出窗口,在Debug Trace 部分将会显示Test thread和Workflow thread信息
    • 很明显,因为WorkflowInvoke使用与调用程序相同的线程来运行工作流,这两个值将永远一样

 

任务 3 – 使用WorkflowApplication类来启动工作流,并获取工作流运行的线程ID

 

上述的测试有一个漏洞,如果WorkflowThread参数都是返回某个非零值,例如返回1而不是真正的线程ID,测试也能通过。所以我们需要修改这个测试并使之能够真正验证返回的线程ID就是工作流运行时的线程ID。

 

为了验证真正的线程ID,需要使用WorkflowApplication 运行工作流。我们修改测试方法,通过调用WorkflowApplication.Completed 动作(action)来获取工作流线程ID,并将之与返回参数WorkflowThread进行比较。

  • 将上述的测试方法ShouldReturnWorkflowThread 改为ShouldReturnWorkflowThreadInvoke
  • 复制以下代码创建新的ShouldReturnWorkflowThread 方法:

/// <summary>
/// Verifies that the workflow returns an Out Argument
/// Name: WorkflowThread
/// Type: Int32
/// Value: Non-Zero, matches thread used for Completed action
/// </summary>
[TestMethod]
public void ShouldReturnWorkflowThread()
{
    AutoResetEvent sync = new AutoResetEvent(false);
    Int32 actionThreadID = 0;
    IDictionary<string, object> output = null;

    WorkflowApplication workflowApp =
        new WorkflowApplication(
                 new SayHello()
                 {
                     UserName = "Test"
                 });

    // Create an Action<T> using a lambda expression
    // To be invoked when the workflow completes
    workflowApp.Completed = (e) =>
        {
            output = e.Outputs;
            actionThreadID = Thread.CurrentThread.ManagedThreadId;

            // Signal the test thread the workflow is done
            sync.Set ();
        };

    workflowApp.Run ();

    // Wait for the sync event for 1 second
    sync.WaitOne (TimeSpan.FromSeconds(1));

    Assert.IsNotNull (output,
        "output not set, workflow may have timed out");

    Assert.IsTrue (output.ContainsKey("WorkflowThread"),
        "SayHello must contain an OutArgument named WorkflowThread");

    // Don't know for sure what it is yet
    var outarg = output["WorkflowThread"];

    Assert.IsInstanceOfType (outarg, typeof(Int32),
        "WorkflowThread must be of type Int32");

    Assert.AreEqual (actionThreadID, (int)outarg,
        "WorkflowThread should equal actionThreadID");

    Debug.WriteLine("Test thread is " +
                    Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine("Workflow thread is " + outarg.ToString());
}

  • 上述代码实现
    • AutoResetEvent实例来控制事件是否完成并通知调用程序
    • 创建WorkflowApplication实例来启动工作流
    • 指定Completed动作,这里使用了一个lambda表达式创建了一个委托,用于在工作流结束后接收输出参数字典以及使用一个本地变量来保存该委托运行时的线程ID
    • 运行工作流程序实例
    • 进行测试验证
      • 输出参数是否为非空
      • 是否存在WorkflowThread参数
      • WorkflowThread参数是否Int32类型
      • WorkflowThread参数值是否等于实际线程ID
  • 注意:WorkflowApplication.Completed 和WorkflowApplication的其它属性是委托,而不是事件,因此如果要使用这些属性,需要提供一个方法、匿名方法或者lambda表达式

验证

 

  • CTRL+SHIFT+B 编译
  • CTRL+R, A 运行所有测试
  • 验证所有测试应该能够通过
  • 在Test Results窗口,双击ShouldReturnWorkflowThread 测试,看看测试Debug的消息是否与ShouldReturnWorkflowThreadInvoke 不同

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
功能模块如下: 个人办公 内部邮件 系统内部的信息交流、文件交流、信息提醒等 Internet邮件 接口外部邮箱,采用pop3和smtp协议代收代发 单位公告通知 管理公告通知、新闻等信息,可选定特定部门发送 投票 内部信息投票,每个用户拥有一次投票权利 手机短信 发送内部或者外部的手机短信消息,用于提醒等 日程安排 个人日程以日历形式重点体现,到期自动提醒 工作日志 工作过程中的记录以及工作日志等 通讯簿 包含各种分类,主要用于联系使用 个人设置 设置个人参数、邮箱参数、提醒参数、修改密码等 审批流程 新建工作 新建新的审批工作流,选定需要的工作新建即可 我的工作 个人发起的所有工作,包括已审批、未审批的所有工作 待办工作 需要当前用户进行办理或者审批的工作列表 已办工作 当前用户已经办理过的各项工作,包含历史工作 工作查询 查询系统中工作的信息,全面检索 工作监控 管理员监控运行,发现问题,可强制干预 归档工作 已办理完成的工作,自动进行归档 工作委托 当前用户指定办理人,所有待办事项自动提交到委托人办理 流程设置 自定义流程、自定义表单、表单各个字段在流程中权限控制 印章管理 管理公章与私章,查看印章的各项使用日志等 公文收发 接收文件 需要接受的文件,未签收时有颜色标注 传阅文件 传阅发送出去文件 接收分类 按照指定文件夹分类查看 传阅分类 传阅文件夹个人分类查看 工作管理 我的计划 个人工作计划,可共享给其他指定用户协同查看 协同计划 其他用户协同共享查看的工作计划信息 我的汇报 个人工作汇报,可共享给其他指定用户协同查看 协同汇报 其他用户协同共享查看的工作汇报信息 下属任务 我的任务 个人接收到的各项任务,提交工作任务报告 任务分配 对下属用户分派工作任务,实时监控任务情况 下属日程 下属员工的日程安排信息 下属日志 下属员工的工作日志信息 下属计划 下属员工的工作计划信息 下属汇报 下属员工的工作汇报信息 下属邮件 下属员工的内部邮件信息 下属客户 下属员工的客户资料信息 知识文档 个人文件 类同与网络硬盘、个人文件柜存储等文件存储类 单位文件 单位各类文件存储区 项目文件 项目实施、进度、评审等文件存储类 电子刊物 电子刊物等文件存储类 重要文件 特别重要的文件额外存储类 机密文件 机密文件存储类 知识库 各项知识库、技术资料、学习资料、单位规范等 技术文件 技术类文件存储 共享文件 查看各类共享的文件资料 回收站 删除的文件,可恢复或者彻底删除 扩展应用 资产管理 管理固定资产、仪器、办公用品等 培训管理 管理培训信息 会议管理 各项会议会议、上传会议记录 车辆管理 车辆信息、使用、维修、保险、加油等 图书管理 图书信息、借阅、归还等 档案卷库 各类档案分卷库保存、销毁 考试管理 在线考试、可分题库、题目类型、试卷制定、自动评分等 学习管理 在线学习学习心得、领导批注等 客户管理 会员管理 会员信息管理,即为个人类别的客户 我的客户 个人客户资料、联系记录、需求等 客户管理 管理所有客户资料,联系人、联系记录、需求计划等 进销存类 产品管理 产品管理、库存列表显示、库存报警等 销售管理 销售合同、产品销售出库、利润等 采购管理 采购合同、产品采购入库等 供应商管理 管理各个供应商、供应商联系人 项目管理 项目信息 项目各项信息、状态等 评审信息 评审记录 项目进度 项目具体进度安排、时间进度 收款信息 收款具体情况,按照项目分类 项目实施 项目的具体实施情况 报销申请 项目类单独报销,也可使用审批流程中的自定义流程 报销管理 审批报销记录,复杂流程可使用审批流程 项目利润 核算具体利润情况 数据统计 数据各项统计 人力资源 考勤管理 考勤记录、考勤设置、查询、统计等 人事档案 人事基本资料维护 人事合同 合同管理情况 奖惩记录 奖惩各类记录管理 奖惩制度 各类具体制度安排、单位规章制度等。 应聘简历 简历管理,建立单位人才库 面试管理 具体面试情况管理 绩效考核 绩效考核数据上报等。 绩效参数 绩效各项参数的设置 薪酬管理 薪酬各类数据管理 薪酬参数 各个参数设置 报表中心 报表分类 报表分类设置,根据需要可分多个类别 报表管理 动态定制报表、动态数据抓取。报表可自定义。 信息交流 论坛BBS 论坛各个信息、帖子发帖信息 版块设置 版块具体设置 内部聊天室 各个聊天室进入后可自由聊天 聊天室设置 聊天室的基本设置 组织机构 单位信息查询 查询单位基本信息 部门信息查询 查询部门信息 用户信息查询 查询用户个人信息 附件程序 电话区号查询 电话区号查询 邮政编码查询 邮政编码查询,超级链接到网址 列车时刻查询 链接到列车时刻查询网址 公交线路查询 公交线路查询 法律法规查询 法律法规各项资料超级链接查询 休闲游戏 链接到游戏网 万年历 日历基本信息 世界时钟 时间查询 常用网址 常用导航资料 系统管理 单位信息管理 管理单位基本信息 部门信息管理 设置部门机构,支持多分公司,集团化应用 用户信息管理 用户信息设置、下属设置、权限设置 角色信息管理 定义权限角色列表,设置角色权限 红头文件模板 红头文件模板,编辑时直接可调用 系统日志管理 系统日志,登录、删除、查看等各个操作 文件上传设置 文件上传格式参数的设置 系统菜单维护 系统各个菜单管理、节点设置、导航设置

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值