使用C#代码来创建WF应用程序
1:创建ConsoleApplication项目:
创建一个控制台项目,命名为HelloCodeWorkflow.
2: 添加对System.Activities程序集的引用
默认的,一个新的控制台程序并不会应用System.Activities程序集。
3:编写定义Workflow的代码。
using System.Activities;
using System.Activities.Statements;
namespace HelloCodeWorkflow {
class Program {
static void Main(string[] args) {
WorkflowInvoker.Invoke(new HelloWorkflow());
}
}
public class HelloWorkflow:Activity {
public HelloWorkflow() {
this.Implementation = () => new Sequence {
Activities = {
newWriteLine(){Text="Hello Workflow"}
}
};
}
}
}
4:运行它:
设置HelloCodeWorkflow作为启动项目,按Ctrl+F5,和期望的一样,结果和前面的结果一样。
它是怎么工作的。。。
我们使用下面的命名空间:
using System.Activities;
using System.Activities.Statements;
因为WorkflowInvoker类属于System.Activities命名空间,Sequence活动,WriteLine活动属于System.Activities.Statements命名空间。
public class HelloWorkflow:Activity {
public HelloWorkflow() {
this.Implementation = () => new Sequence {
Activities = {
newWriteLine(){Text="Hellow Workflow"}
}
};
}
}
通过实现继承自Activity的一个类,我们通过C#代码定义了Workflow。
WorkflowInvoker.Invoke(s);
上面的代码加载了一个工作流实例,并且自动的运行它,WorkflowInvoker.Invoke方法是同步的,这也就是说它使用调用者的线程来执行workflow.
还有更多。。
WF4 也提供了一个类DynamicActivity,通过它我们可以在运行时动态的来定义工作流实例,换句话说,通过使用DynamicActivity,我们没有必要在工作流的初始化之前就定义工作流。下面是一些样例代码:
public static DynamicActivity GetWF() {
return new DynamicActivity() {
Implementation = () => new Sequence() {
Activities ={
newWriteLine(){Text="Hello Workflow"}
}
}
};
}
使用InArguments 来初始化WF程序。
在这个任务中,我们创建一个可以接收参数的工作流程序,我们使用InArguments来定义工作流需要的参数。
创建工作流程序
1:创建工作流程序:
创建一个新的工作流控制台程序,命名为UseInArgument
2:制作WF程序。
在这里定义了工作流需要的两个参数,参数的方向是In(输入).类型是String.
接着使用WriteLine方法输出这两个参数。
3:编写代码启动工作流。
修改Program.cs 代码如下:
using System.Activities;
using System.Activities.Statements;
namespace UseInArgument {
class Program {
static void Main(string[] args) {
WorkflowInvoker.Invoke(new Workflow1()
{
FirstName="Andrew",
SecondName="Zhu"
});
}
}
}
4:运行:
设置UseInArgument作为启动项目,按Ctrl+F5,结果如下:
它是怎么工作的。。。
考虑下我们在Main方法中的语句:
FirstName="Andrew"
FirstName的类型是InArgument,但是我们怎么可以不通过显示的转换就可以将String赋值给InArgumen呢?,这是因为InArgument使用了
System.ComponentModel.TypeConverterAttribute(System.Activities.XamlIntegration.InArgumentConverter)特性来标记的。
就是InArgumentConverter使得可以直接将string赋值给InArgument类型。如果你想了解更多关于TypeConverter,可以查看MSDN的:
http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx
还有更多:
在WF3/3.5 中,给一个Wrorkflow传递参数是通过Dictionary<T>类的,这种方式在WF4中也可以。
using System.Activities;
using System.Activities.Statements;
using System.Collections.Generic;
namespace UseInArgument {
class Program {
static void Main(string[] args) {
IDictionary<string, object> inputDictionary =
new Dictionary<string,object>()
{
{"FirstName","Andrew"},
{"SecondName","Zhu"}
};
WorkflowInvoker.Invoke(new Workflow1(),
inputDictionary);
}
}
}
如果你是使用C#代码来创建的工作流,我们可以像下面这样使用InArgument参数
public classWorkflowInCode : Activity
{
public InArgument<string>FirstName { get; set;}
public InArgument<string>SecondName { get; set;}
publicWorkflowInCode()
{
this.Implementation= () =>new Sequence()
{
Activities =
{
newWriteLine(){ Text=newInArgument<string>(
activityContext=>"My name is "+FirstName.Get(activityContext))},
newWriteLine(){Text=newInArgument<string>(
activityContext=>SecondName.Get(activityContext))}
}
};
}
}
创建一个使用OutArgument参数的工作流程序
1:创建工作流控制台程序,命名为UseOutArgument
2:制作WF程序:
在这里,定义了一个OutMessage的输出参数。使用A-B Assign活动来将OutMessage设置为”This is a message.”
3:编写调用WF的代码:
using System;
using System.Activities;
using System.Collections.Generic;
namespace UseOutArgument {
class Program {
static void Main(string[] args) {
IDictionary<string,object> output=
WorkflowInvoker.Invoke(newWorkflow1());
Console.WriteLine(output["OutMessage"]);
}
}
}
4:设置UseOutArgument作为启动项目,按Ctrl+F5运行程序,结果如下:
它是怎样工作的。。。
看看下面的代码片段:
IDictionary<string,object> output=
WorkflowInvoker.Invoke(new Workflow1());
Console.WriteLine(output["OutMessage"]);
OutMessage是我们在Workflow1.xaml中定义的输出参数
(OutArgument),WorkflowInvoker.Invoke方法返回一个IDictionary类型的对象,里面包含了工作流的输出参数。
还有更多...
还有第三种类型:InOutArgument,它用来绑定一个活动的输入和输出参数,在大部分的情况下,我们可以使用InOutArgument来代替InArgument和OutArgument,但是他们还是有一些不同的地方---例如,我们不能直接将一个String赋值给InOutArgument,但是却可以直接将String赋值给InArgument。
创建一个使用InOutArgument的WF程序
1:创建工作流控制台程序:UseInOutArgument
2:制作工作流程序:
Workflow包含一个InOutArgument“InOutMessage“,WriteLine活动输出参数,Assign活动为InOutMessage赋值。
3:修改Main函数如下:
class Program
{
static void Main(string[]args)
{
IDictionary<string,object>input = newDictionary<string,object>()
{
{"InOutMessage","Now,I am InMessage"}
};
IDictionary<string,object>output =
WorkflowInvoker.Invoke(newWorkflow1(),input);
Console.WriteLine(output["InOutMessage"]);
Console.ReadLine();
}
}
4:运行,
设置UseInOutArgument作为启动项目按Ctrl+F5 运行,结果如下:
它是怎么工作的。。。
下面的代码定义了输入参数:
IDictionary<string, object> input =
new Dictionary<string,object>()
{
{"InOutMessage","Now, I am InMessage"}
};
下面的代码返回工作流程序的结果:
IDictionary<string,object> output=
WorkflowInvoker.Invoke(newWorkflow1(),input);
还有更多。。。
我们不能直接将String赋值给InOutArgument,所以下面的代码是不允许的:
IDictionary<string, object> output =
WorkflowInvoker.Invoke(new Workflow1()
{
InOutMessage="Now,I am InMessage"
});
在WF程序中使用变量
我们可以在WF程序运行的时候使用一些变量来临时的保存一些值,在这个任务中,我们创建一个在控制台中循环输出5个数字的WF程序,我们将使用NumberCounter变量来作为计数器。
1:创建工作流控制台程序:UseVariable.
2:添加一个Sequence活动,然后点击Sequence活动,创建一个Int32类型的NumberCounter变量,设置变量的Scope为Sequence,然后按下面的截图制作workflow,在第二个Assign活动中输入NumberCounter= NumberCounter +1.
3:运行:
设置UseVariable为启动项目,按Ctrl+F5运行,结果如下:
它是怎样工作的。。。
为了让workflow的逻辑更容易理解,可以转换工作流为C#代码,如下:
int NumberCounter = 0;
do
{
Console.WriteLine(NumberCounter);
NumberCounter++;
}while (NumberCounter <= 5);
虽然我们可以使用参数(arguments)来将数据流入流出工作流,我们也可以使用变量(Variable)来储存在workflow中的一些临时数据,每一个variable都有它的作用域,只有作用域内的活动可以操作这些变量。
还有更多。。。
请明白,我们不能在外部的宿主环境中访问到工作流中定义的变量,WF4的变量主要是在工作流的实例之间临时的保存数据使用的。要在外部宿主中访问工作流我们可以使用书签(Bookmark)
异步的运行WF程序
在前面的实例中,我们使用WorkflowInvoker.Invoke方法来同步的启动工作流,它很容易使用,但是有些时候,尤其是在真实的应用程序中,一个工作流应该运行在另一个独立的线程中,在这个示例中,我们使用WorkflowApplication来运行一个工作流。
1:创建工作流控制台程序,命名为UseWorkflowApplication.
2:制作workflow,
打开Workflow1.xaml的设计器,点击Arguments,创建两个Int32的InArguments,名称为Number1,Number2,接着创建一个Int32的OutArgument,名称为Result。添加一个Assign活动。输入Result = Number1 + Number2
3:Main函数如下:
class Program
{
static void Main(string[]args)
{
IDictionary<string,object>output =
WorkflowInvoker.Invoke(newWorkflow1() {Number1 = 123, Number2 = 456 });
Console.WriteLine(output["Result"]);
AutoResetEventsyncEvent =new AutoResetEvent(false);
IDictionary<string,object>input =
newDictionary<string,object>()
{
{"Number1",123},
{"Number2",456}
};
IDictionary<string,object>output2 = null;
WorkflowApplicationwfApp =new WorkflowApplication(
newWorkflow1(), input);
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgse)
{
Console.WriteLine("Workflow thread id:" +Thread.CurrentThread.ManagedThreadId);
output2 = e.Outputs;
syncEvent.Set();
};
wfApp.Run();
Console.WriteLine("workflow start execute");
syncEvent.WaitOne();
Console.WriteLine(output2["Result"]);
Console.WriteLine("Host thread id:" +Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();
}
}
4:运行:结果如下:
它怎样工作。。。
AutoResetEvent syncEvent = newAutoResetEvent(false);
当工作流线程和控制台主线程同时运行的时候,主线程可能会在工作流线程结束前终止,为了防止这种情况发生,我们使用AutoResetEvent来对主线程和工作流线程进行同步
syncEvent.WaitOne();
上面的语句,会让主线程一直等待,直到工作流syncEvnet set的时候。
wfApp.Completed =
delegate(WorkflowApplicationCompletedEventArgse)
{
output = e.Outputs;
syncEvent.Set();
};
通过匿名委托,在wfApp 的Completed事件发生的时候,就会调用syncEvent的Set方法了。
自定义一个使用书签(Bookmark)的MyReadLine活动
通过使用InArgumen,OutArgument和InOutArgument,我们可以在工作流启动和结束的时候流动数据(输入数据给工作流,从工作流中获取数据作为结果),但是怎样可以在工作流运行的时候传递数据给工作流呢? --Bookmark可以帮助我们实现这个功能,在这个示例中,我们将创建一个使用Bookmark的MyReadLine的活动
1:创建工作流控制台项目,命名为UseBookmark,接着添加一个MyReadLineActivity的类,
2:修改MyReadLineActivity的代码如下:
using System.Activities;
namespace UseBookmark{
public class MyReadLine : NativeActivity<string>{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(
NativeActivityContext context)
{
context.CreateBookmark(BookmarkName.Get(context),
newBookmarkCallback(OnResumeBookmark));
}
protected override bool CanInduceIdle
{
get
{
{ return true;}
}
}
public void OnResumeBookmark(
NativeActivityContext context,
Bookmark bookmark,
object obj)
{
Result.Set(context, (string)obj);
}
}
}
保存文件,然后按F6,这样这个活动就可以出现在工作流设计器的工具箱中了。
(注意:MyReadLineActivity类是public的,否则工具箱中将找不到该活动)
3:创作工作流如下:
4:编写代码来宿主工作流:
class Program
{
static void Main(string[]args)
{
AutoResetEventsyncEvent =new AutoResetEvent(false);
stringbookmarkName = "GreetingBookmark";
WorkflowApplicationwfApp =new WorkflowApplication(
newWorkflow1() { BookmarkNameInArg =bookmarkName });
wfApp.Completed = (completedEventArgs)=> { syncEvent.Set(); };
wfApp.Run();
Thread.Sleep(2000);
wfApp.ResumeBookmark(bookmarkName,Console.ReadLine());
syncEvent.WaitOne();
Console.ReadLine();
}
}
5:运行,结果如下:
它怎样工作。。。
在上面的第二步中,我们创建了一个从NativeActivity继承的类,NativeActivity是可以用来创造复杂活动的一个特殊的抽象活动,我们在Chapter 5的Custom Activities中再讨论。
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(OnResumeBookmark));
这个语句,WF上下文创建了带BookmarkName和BookmarkCallback的Bookmark,当wfApp.ResumeBookmark方法调用的时候,定义在自定义活动中的 OnResumeBookmark 方法就会被执行。
protected override bool CanInduceIdle{
get
{
{ return true;}
}
}
这个是内建的属性,用来判断自定义工作流是否可以变得空闲状态(idle).默认值是false。
考虑下step3 的下面的代码:
wfApp.ResumeBookmark(bookmarkName,
Console.ReadLine());
当这条语句执行的时候,定义在MyReadLine活动中的 OnResumeBookmark方法就会被执行,并且接受Console.ReadLine()的值作为参数。
可以将上面的代码拆开来:
string resumeValue=Console.ReadLine();
wfApp.ResumeBookmark(bookmarkName,resumeValue);
小结:变量、参数、书签
1、 变量是用来临时保存是数据
2、 参数是用来传入传出数据
3、 BookMark是用于在工作流执行的时候传递数据给工作流
本文介绍了如何使用C#代码创建Windows Workflow Foundation (WF) 应用程序,包括创建简单的工作流、使用InArguments、OutArguments和InOutArguments参数,以及在工作流中使用变量和书签。示例代码展示了如何创建和运行工作流,以及如何进行异步执行和数据交互。重点讲解了参数和变量在工作流中的作用,以及书签在传递数据给工作流时的功能。

936

被折叠的 条评论
为什么被折叠?



