现在的workflow foundation是bate2.2版。比之上一个版本,改变也比较大。
现在的微软提供了很多activty,也有新的在发布,这里是他的老家:
http://www.windowsworkflow.net/Default.aspx?tabindex=0&tabid=1
我的这个例子比较简单,和一个老外的代码差不多。
首先,需要做的有三部分
1:要处理的对象,和wrokflow的接口,事件。
2:设计wrokflow
3:作host,驱动workflow的运行。
我的这个例子是一个文档批准的流程:创建文档(草稿状态);确认提交(提交状态);批准(结束状态);拒绝(返回草稿状态)。
按照上边的步骤,开始做
1:添加对象,接口,事件
添加一个Document的对象
添加一个可以和workflow交互的接口
IDocumentService
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Workflow.ComponentModel;
5using System.Workflow.Activities;
6
7namespace StateWorkflowLib.App.DocumentWF
8{
9 //Serializable for persis
10 [Serializable]
11 public class DocumentEventArgs : ExternalDataEventArgs
12 {
13 private string _docId;
14
15 public DocumentEventArgs(Guid instanceId, string docId)
16 : base(instanceId)
17 {
18 _docId = docId;
19 }
20
21 public string DocId
22 {
23 get { return _docId; }
24 set { _docId = value; }
25 }
26 }
27
28 [ExternalDataExchange]
29 public interface IDocumentService
30 {
31 event EventHandler<DocumentEventArgs> DocumentCreated;
32 event EventHandler<DocumentEventArgs> DocumentSubmited;
33 event EventHandler<DocumentEventArgs> DocumentApproved;
34 event EventHandler<DocumentEventArgs> DocumentRejected;
35 }
36}
注意 :DocumentEventArgs 要继承自ExternalDataEventArgs;还要可以Serializable。因为这类对象可能需要序列化,存到数据库;
IDocumentService接口是要有ExternalDataExchange属性的,用来做数据交换。
然后定义一个实现
DocumentService
1 [Serializable]
2 public class DocumentService:IDocumentService
3 {
4 public void RaiseDocumentCreatedEvent(DocumentObject doc, Guid instanceId)
5 {
6 //to do : ur business logic
7 // doc.Save();
8
9 //raise envent to invoke workflow run
10 if (this.DocumentCreated != null)
11 this.DocumentCreated(null, new DocumentEventArgs(instanceId, doc.ID.ToString()));
12 }
13
14 public void RaiseDocumentCreatedEvent(string docId, Guid instanceId)
15 {
16 //
17 //to do: another logic here
18 //
19 if (this.DocumentCreated != null)
20 this.DocumentCreated(null, new DocumentEventArgs(instanceId, docId));
21 }
22
23 public void RaiseDocumentSubmitedEvent(string docId, Guid instanceId)
24 {
25 if (this.DocumentSubmited != null)
26 this.DocumentSubmited(null, new DocumentEventArgs(instanceId, docId));
27 }
28 public void RaiseDocumentApprovedEvent(string docId, Guid instanceId)
29 {
30 if (this.DocumentApproved != null)
31 this.DocumentApproved(null, new DocumentEventArgs(instanceId, docId));
32 }
33 public void RaiseDocumentRejectedEvent(string docId, Guid instanceId)
34 {
35 if (DocumentRejected != null)
36 DocumentRejected(null, new DocumentEventArgs(instanceId, docId));
37 }
38
39 public event EventHandler<DocumentEventArgs> DocumentCreated;
40 public event EventHandler<DocumentEventArgs> DocumentSubmited;
41 public event EventHandler<DocumentEventArgs> DocumentApproved;
42 public event EventHandler<DocumentEventArgs> DocumentRejected;
在这个service里面可以写自己的普通的处理代码,然后引发事件,驱动workflow的运转。
注意,需要继承自刚才定义的那个接口
2:配置workflow
这里的配置选用的是xoml的statewrokflow。这样的wrokflow其实定义的是一个文件。
定义的结果如下
<
StateMachineWorkflowActivity x:Class
=
"
StateWorkflowLib.XmlWorkflowExample
"
InitialStateName
=
"
InitState
"
x:Name
=
"
XmlWorkflowExample
"
DynamicUpdateCondition
=
"
{x:Null}
"
CompletedStateName
=
"
ClosedState
"
xmlns:x
=
"
http://schemas.microsoft.com/winfx/2006/xaml
"
xmlns
=
"
http://schemas.microsoft.com/winfx/2006/xaml/workflow
"
>
<
StateActivity x:Name
=
"
InitState
"
>
<
EventDrivenActivity x:Name
=
"
waitForCreate
"
>
<
HandleExternalEventActivity x:Name
=
"
handleCreateEvent
"
EventName
=
"
DocumentCreated
"
InterfaceType
=
"
{x:Type StateWorkflowLib.App.DocumentWF.IDocumentService,TypeName=StateWorkflowLib.App.DocumentWF.IDocumentService}
"
/>
<
SetStateActivity x:Name
=
"
setToDraftState
"
TargetStateName
=
"
DraftState
"
/>
</
EventDrivenActivity
>
</
StateActivity
>
<
StateActivity x:Name
=
"
DraftState
"
>
<
EventDrivenActivity x:Name
=
"
waitForSubmit
"
>
<
HandleExternalEventActivity x:Name
=
"
handleSubmitEvent
"
EventName
=
"
DocumentSubmited
"
InterfaceType
=
"
{x:Type StateWorkflowLib.App.DocumentWF.IDocumentService,TypeName=StateWorkflowLib.App.DocumentWF.IDocumentService}
"
/>
<
SetStateActivity x:Name
=
"
setToSubmit
"
TargetStateName
=
"
SubmitedState
"
/>
</
EventDrivenActivity
>
</
StateActivity
>
<
StateActivity x:Name
=
"
SubmitedState
"
>
<
EventDrivenActivity x:Name
=
"
waitForApprovedEvent
"
>
<
HandleExternalEventActivity x:Name
=
"
handleApprovedEvent
"
EventName
=
"
DocumentApproved
"
InterfaceType
=
"
{x:Type StateWorkflowLib.App.DocumentWF.IDocumentService,TypeName=StateWorkflowLib.App.DocumentWF.IDocumentService}
"
/>
<
SetStateActivity x:Name
=
"
setToClosed
"
TargetStateName
=
"
ClosedState
"
/>
</
EventDrivenActivity
>
<
EventDrivenActivity x:Name
=
"
waitForRejectedEvent
"
>
<
HandleExternalEventActivity x:Name
=
"
handleRejectedEvent
"
EventName
=
"
DocumentRejected
"
InterfaceType
=
"
{x:Type StateWorkflowLib.App.DocumentWF.IDocumentService,TypeName=StateWorkflowLib.App.DocumentWF.IDocumentService}
"
/>
<
SetStateActivity x:Name
=
"
setToDraft
"
TargetStateName
=
"
DraftState
"
/>
</
EventDrivenActivity
>
</
StateActivity
>
<
StateActivity x:Name
=
"
ClosedState
"
/>
</
StateMachineWorkflowActivity
>
每一个workflow必须要有一个开始,一个结束状态;
这里定义了4个状态:WaitForCreate,Draft,Sumbmited,Closed。
拿其中一个作为例子,WaitForCreate状态有一个EventDrivenActivity,等待事件驱动的一个功能。然后这个里面有一个订阅外部事件的处理HandleExternalEventActivity,订阅的是“DocumentCreated”事件;接收到这个事件之后(可以做很多处理)接着就直接改变了当前工作流的状态到Darft状态。
3:写aspx驱动之
首先,配置webconfig。注意在web里面要使用 <add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
不要使用DefaultWorkflowSchedulerService。需要的话,还要定义SqlWorkflowPersistenceService,用来把工作流保存到数据库中。
然后,启动一个WorkflowRuntime
_workflowRuntime
=
new
System.Workflow.Runtime.WorkflowRuntime(
"
WorkflowRuntime
"
);
workflowRuntime.StartRuntime();
注意,里面的参数是在webconfig中配置的节点的名字(NodeName,而不是name属性)
如果是第一次启动工作流
DocumentService docService
=
WorkflowManager.GlobalDataExchangeService.GetService(
typeof
(DocumentService))
as
DocumentService;
if
(docService
==
null
)
{
docService = new DocumentService();
WorkflowManager.GlobalDataExchangeService.AddService(docService);
WorkflowInstance workflowInstance = WorkflowManager.WorkflowRuntime.CreateWorkflow(typeof(XmlWorkflowExample));
workflowInstance.Start();
page.Session["DocumentWorkflowId"] = workflowInstance.InstanceId;
}
WorkflowManager是我自己写的一个类,因为workflow现在的版本未定,总是在变,所以自己写一个接口。
基本意思就是,如果第一次运行,那么就要生成一个docService实例,交给工作流管理,然后生成一个工作流实例。记录下这个实例的Id。
这是一个具体驱动workflow运行的代码
Guid instanceId
=
new
Guid(page.Session[
"
DocumentWorkflowId
"
].ToString());
switch
(eventName)
{
case "CreateDocument":
//to do logic here
//DocumentObject docObj = new DocumentObject();
//docService.RaiseDocumentCreatedEvent(docObj, instanceId);
//
docService.RaiseDocumentCreatedEvent(docId, instanceId);
WorkflowManager.GlobalManualWorkflowSchedulerService.RunWorkflow(instanceId);
这一个可以写在哪一个Button的处理里面了。需要注意的是WorkflowSchedulerService,这里需要手工驱动工作流运转。因为在web环境下,不能使用DefaultWorkflowSchedulerService,不然的话,只要docService.RaiseDocumentCreatedEvent(docId, instanceId)一执行,工作流就直接驱动起来了,不要自己再去RunWorkflow的。
写了不少,但是有点太乱了。
workflow fundation的特点 / 如何将工作流保存到数据库(一个LongRun工作流的话)只能下次再说了。