VS2010 学习笔记 WF4 (6) 使用WorkflowApplication启动工作流
2010-03-29 10:42
357 查看
到目前我们知道如何创建一个工作流节点(activity)和使用最简单的方法来启动工作流,就是使用WorkflowInvoker
类。WorkflowInvoker.Invoke
方法比较简单,它属于同步调用,工作流启动的线程与调用程序的线程是一样的。
启动工作流另外一个方法是使用WorkflowApplication
类,相对WorkflowInvoke类,它可以允许工作流在另外一个独立线程运行,提供了当工作流结束的时候可以调用的delegates。比起不使用WF4,可以更加简单和快捷的创建多线程服务器或客户端程序。
这节会学习到,修改启动程序用WorkflowApplication来运行SayHello节点,并观察线程表现。要求可以达到以下功能:
返回一个个性化的问候语
返回一个非零的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,所以要输入(Import
)System.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
不同
类。WorkflowInvoker.Invoke
方法比较简单,它属于同步调用,工作流启动的线程与调用程序的线程是一样的。
启动工作流另外一个方法是使用WorkflowApplication
类,相对WorkflowInvoke类,它可以允许工作流在另外一个独立线程运行,提供了当工作流结束的时候可以调用的delegates。比起不使用WF4,可以更加简单和快捷的创建多线程服务器或客户端程序。
这节会学习到,修改启动程序用WorkflowApplication来运行SayHello节点,并观察线程表现。要求可以达到以下功能:
返回一个个性化的问候语
返回一个非零的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,所以要输入(Import
)System.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
不同
相关文章推荐
- VS2010 学习笔记 WF4 (4) 使用xaml实现动态工作流
- VS2010 学习笔记 WF4 (9) 工作流基础总结
- AM335x(TQ335x)学习笔记——使用dtb方式启动内核
- 【VS2010学习笔记】【异常处理】一(无法启动此程序,因为计算机中丢失libiconv-2.dll)
- php学习笔记1 apache服务器使用及配置① 启动和停止 端口配置 apache的目录结构
- Maven学习笔记——(2)使用Maven的Tomcat插件启动web项目
- VS2010 学习笔记 WF4 (2) 修改默认名称
- 【VS2010学习笔记】【编程实例】 (在Visual Studio中使用C++创建和使用DLL)
- VS2010 学习笔记 WF4 (3) 学习CodeActivity
- VS2010 学习笔记 WF4 (5) 加入单元测试,实现测试驱动开发(TDD)
- AM335x(TQ335x)学习笔记——使用dtb方式启动内核
- VS2010 学习笔记 WF4 (7) 学习If/Else逻辑节点,根据输入参数的长度来返回不同的问候语
- vs2010 学习Silverlight学习笔记(8):使用用户控件
- 韩顺平PHP学习视频笔记整理026apache服务器使用及配置① 启动和停止 端口配置
- VS2010 学习笔记 WF4 (1) 第一个Workflow程序:Hello Workflow
- VS2010 学习笔记 WF4 (8) 在工作流中进行错误捕捉和处理
- SharePoint开发学习笔记4——使用aspx自定义表单的工作流(1)
- Xcode9学习笔记79 - 使用Tread暂停动作的执行(让启动画面停留2秒再载入主界面)
- 【VS2010学习笔记】【编程实例】 (在Visual Studio中使用C++创建和使用DLL)
- 一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(6)--创建启动和关联表单