我觉得,WF给我们带来的主要有几点是值得了解的
流程设计的可以只管流程,他们不要知道怎么做。流程设计的人员可能(或者一定)不会用Visual Studio,他们可以使用一些简单的设计器工具。例如下面的这个小例子就是一个独立的程序,上面包装了WF的设计器。结合自定义Activity的设计,可以将业务逻辑隐藏起来。
这是很多人疑惑的,他们觉得既然有Workflow Foundation,就万事大吉了。因为工作流实例确实也有数据,而且我们可以持久化将其保存起来嘛。
停!这不是一个正确的想法。工作流的持久化服务顾名思义,其实主要是为了给我们维护长时间工作的流程信息的(可以在空闲的时候卸载,保存到数据库等)。
大家应该这样理解,Workflow Foundation只是管流程的部分,它不管数据。
没错,它只管流程。这有什么问题么,它管好这个就够了,而且确实能帮很大的忙,不是吗?
这样,我们就可以将注意力放在业务数据的管理,而不是流程状态的管理之类。
请注意上面选中的项目,我来解释一下
3.1 WorkflowLibrary 这个项目包含了工作流设计,它是可视化设计的成果。下图是一个典型的审批流程
3.2 OrderApprovalEventType 这个项目包含了工作流设计时可能会用到的一些接口和事件定义。为什么需要用事件呢?一般我们的流程如果需要等待用户干预,诸如审批之类的情况,就需要这样做,因为它可能不是立即发生的。
注意,接口要标记为ExternalDataExchange,事件参数需要继承ExternalDataEventArgs,而且必须可序列化
3.3 Contracts 这个项目是标准的WCF合约定义,因为我们是分了服务器和客户端的,他们之间通过WCF通讯,包括创建流程,以及激发事件等等
3.4 Services,这就是具体实现的WCF服务,在这里可以启动工作流运行时,并且按照客户端指令做相应的事情。这里的代码是相当多的。
using System; using System.Collections.Generic; using System.Linq; using Contracts; using System.ServiceModel; using System.Workflow.Runtime; using OrderApprovalEventType; using System.Workflow.Activities; using System.Workflow.Runtime.Hosting; using System.Workflow.Runtime.Tracking; namespace Services { [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]//只有一个实例 public class OrderService:IOrderService { //运行时 private WorkflowRuntime runtime = null; //所有实例 private List instances = new List (); //本地服务 LocalService local = null; public OrderService() { runtime = new WorkflowRuntime(); //添加数据交换服务,可以激发事件 ExternalDataExchangeService svr = new ExternalDataExchangeService(); runtime.AddService(svr); local = new LocalService(); svr.AddService(local); //添加持久化服务,保存实例 //F:/Windows/Microsoft.NET/Framework/v3.0/Windows Workflow Foundation/SQL/zh-CHS这里会有脚本 var db = "server=(local);database=WorkflowFoundation;integrated security=true"; SqlWorkflowPersistenceService sqlsvc = new SqlWorkflowPersistenceService(db); runtime.AddService(sqlsvc); //添加跟踪服务,可以对服务进行诊断和调试 SqlTrackingService tracksvc = new SqlTrackingService(db); runtime.AddService(tracksvc); //绑定有关的事件 runtime.WorkflowStarted += new EventHandler (runtime_WorkflowStarted); runtime.WorkflowCompleted += new EventHandler (runtime_WorkflowCompleted); runtime.WorkflowCreated += new EventHandler (runtime_WorkflowCreated); runtime.WorkflowAborted += new EventHandler (runtime_WorkflowAborted); runtime.WorkflowTerminated += new EventHandler (runtime_WorkflowTerminated); runtime.WorkflowUnloaded += new EventHandler (runtime_WorkflowUnloaded); runtime.StartRuntime(); //启动运行时 Console.WriteLine( "工作流服务器已经准备就绪"); //加载那些保存好的流程实例 foreach (var item in sqlsvc.GetAllWorkflows()) { var instance =(WorkflowLibrary.Workflow1)runtime.GetWorkflow(item.WorkflowInstanceId).GetWorkflowDefinition(); instances.Add( new WorkflowIntanceData() { Id = item.WorkflowInstanceId, Amount = instance.Amount }); } } void runtime_WorkflowUnloaded( object sender, WorkflowEventArgs e) { Console.WriteLine( "时间:{0},卸载流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } void runtime_WorkflowTerminated( object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine( "时间:{0},终止流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } void runtime_WorkflowAborted( object sender, WorkflowEventArgs e) { Console.WriteLine( "时间:{0},中断流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } void runtime_WorkflowCreated( object sender, WorkflowEventArgs e) { Console.WriteLine( "时间:{0},创建流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } void runtime_WorkflowCompleted( object sender, WorkflowCompletedEventArgs e) { var found = instances.FirstOrDefault(d => d.Id == e.WorkflowInstance.InstanceId); if (found != null) instances.Remove(found); Console.WriteLine( "时间:{0},完成流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } void runtime_WorkflowStarted( object sender, WorkflowEventArgs e) { Console.WriteLine( "时间:{0},启动流程:{1}", DateTime.Now, e.WorkflowInstance.InstanceId); } #region IOrderService 成员 public void StartRequest(Guid id, int amount) { //准备数据 var initParam = new Dictionary< string, object>(); initParam.Add( "Amount",amount); //创建实例 var instance = runtime.CreateWorkflow( typeof(WorkflowLibrary.Workflow1), initParam, id); //保存有关数据 instances.Add( new WorkflowIntanceData(){ Id = id, Amount = amount, RequestEmployee= "陈希章" }); //启动实例 instance.Start(); } public void ProcessRequest(Guid id, OrderApprovalEventType.ProcessResult result, string notes) { var args = new ProcessEventArgs(id); args.Result = result; args.Notes = notes; //从集合中删除掉有关的实例 var found = instances.FirstOrDefault(d => d.Id == id); if (found != null) instances.Remove(found); local.RaiseEvent(args); //激发事件 } public List GetWorkflowInstances() { return instances.Where(d => d.Amount >= 2000).ToList(); } #endregion #region IOrderService 成员 public void UnLoad(Guid id) { runtime.GetWorkflow(id).Unload(); //要求开启MSDTC服务 } #endregion ~OrderService() { //析构的时候将所有未完成的工作流实例保存起来 foreach (var item in runtime.GetLoadedWorkflows()) { item.Unload(); } } public void UnloadAllInstances() { foreach (var item in runtime.GetLoadedWorkflows()) { item.Unload(); } } } [Serializable] //这个必须标记为可序列化 internal class LocalService : ManagerEvent { #region ManagerEvent 成员 public event EventHandler Process; #endregion internal void RaiseEvent(ProcessEventArgs e) { //这里可能要更新数据库 if (Process != null) Process( this, e); } } }
注意:这里有一个所谓本地服务的概念,是要实现第二步的那个接口,并编写有关触发事件的代码
3.5 HostService 如上都准备好之后,接下来就是通过一定的方式托管这些服务了。我们可以采用Windows Service来托管
3.6 SimpleClient 最后当然少不了要有客户端界面来实现一些操作。我们这里使用了Windows Forms作为界面。
using System; using System.Windows.Forms; using System.ServiceModel; using Contracts; namespace SimpleClient { public partial class Form1 : Form { IOrderService proxy = null; public Form1() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); ChannelFactory factory = new ChannelFactory ( new BasicHttpBinding(), new EndpointAddress( "http://localhost:8080/OrderService")); proxy = factory.CreateChannel(); } private void btRequest_Click( object sender, EventArgs e) { int amount = int.Parse(txtAmount.Text); proxy.StartRequest(Guid.NewGuid(), amount); MessageBox.Show( "已经发起了一个订单"); } private void btGet_Click( object sender, EventArgs e) { dgvRequests.DataSource = proxy.GetWorkflowInstances(); } private void dgvRequests_CellEnter( object sender, DataGridViewCellEventArgs e) { btOK.Enabled = btCancel.Enabled = true; } private void dgvRequests_CellLeave( object sender, DataGridViewCellEventArgs e) { //btOK.Enabled = btCancel.Enabled = false; } private void btOK_Click( object sender, EventArgs e) { var row = dgvRequests.Rows[dgvRequests.SelectedCells[0].RowIndex]; var id = (Guid)row.Cells[0].Value; proxy.ProcessRequest( id, OrderApprovalEventType.ProcessResult.Approval, txtNotes.Text); } private void btCancel_Click( object sender, EventArgs e) { var row = dgvRequests.Rows[dgvRequests.SelectedCells[0].RowIndex]; var id = (Guid)row.Cells[0].Value; proxy.ProcessRequest( id, OrderApprovalEventType.ProcessResult.Reject, txtNotes.Text); } private void btUnload_Click( object sender, EventArgs e) { var row = dgvRequests.Rows[dgvRequests.SelectedCells[0].RowIndex]; var id = (Guid)row.Cells[0].Value; proxy.UnLoad(id); } } }
这样,一套工作流解决方案就做好了。这个架构可以供很多朋友参考
另外,下面有几篇有关的文章可以参考学习
http://blogs.msdn.com/cnflow/archive/2010/04/16/9996989.aspx
工作流内部工作原理(二)
http://blogs.msdn.com/cnflow/archive/2010/04/16/9996992.aspx
工作流内部工作原理(三)
http://blogs.msdn.com/cnflow/archive/2010/04/16/9996995.aspx