最近正在学习工作流,正好从网上搜索到一个 Asp.net工作流(WWF+LINQ) 的例子,之前学习MOSS时接触过工作流,不过那是针对MOSS的工作流,我一直从事B/S架构开发,知道工作流可用于很多环境,其中就包含asp.net,这让我非常想做一个这样的实例。看了它的源码,大部分都觉的非常不错,不过也有些个人认为不太满意的地方,这可能是因为作者考虑到只是一个简单的demo,没有必要注意一些非技术方面的细节问题。


      项目介绍:一个报销二级审批工作流,数据层操作采用LINQ TO SQL,虽然MS不再发展示它。


          第一级:员工提交报销表给PM(经理),如果数目大于1000RMB,经理如果选择通过审批,工作流会到副总的二级审批,同时经理可以直接选择拒绝。


          第二级:PV(副总),副总收到PM的审批后,可以选择通过或者是拒绝,如果通过,工作流会提交到财务。由财务最后结束工作流。

      流程图如下:

附件: 2009-2-25-3.jpg


      我创建的工作流与原作者的源码有以下几点不同:


          1:原文的工作流采用的是状态机工作流,而这里我采用顺序工作流。 


          2:把数据操作部分和业务逻辑以及页面层功能完全分开,即,只要是和数据访问,操作相关的代码只允许出现在数据处理层中,而业务逻辑层以及页面层均不允许,页面层只和业务逻辑层沟通,不允许直接访问数据处理层。


          3:工程的命名上有改动,例如:


                解决方案名称:ApproveWorkFlow, 页面层叫ApproveWorkFlow.Web。


          4:对相关的方法做了适当的改进。


          5:增加方法以及文档注释。

      项目结构图如下:

附件: 2009-2-25-1.jpg



      项目结构说明:


          1:ApproveWorkFlow.BLL:业务逻辑层。

          2:ApproveWorkFlow.Common,可以放些常用的方法,就是常说的工具类。

          3:ApproveWorkFlow.DAL,数据处理层,数据的增删改查。

          4:ApproveWorkFlow.Data,数据库的连接类,这里我放了一个dbml文件。

          5:ApproveWorkFlow.Model,实体类。

          6:ApproveWorkFlow.MyInterface,接口层。

          7:ApproveWorkFlow.MyWorkFlow,工作流。

          8: ApproveWorkFlow.Web,页面层。

        WF中的持久化服务:

            SqlWorkflowPersistenceService是WF框架中的一个SQL持久性服务(支持SQL Server2005)。在安装DotNet时并不会自动安装此类所需要的数据库。要正确使用此类必需执行以下步骤:


            1:打开Sql Server Management Stuio。


            2:新建一个数据库SqlPersistenceService,这个名字可以更改。


            3:执行相关数据库脚本:


                1:%WINDOWS%"Microsoft.NET"Framework"v3.0"Windows WorkflowFoundation"SQL"EN"SqlPersistenceService_Schema.sql。


                2:%WINDOWS%"Microsoft.NET"Framework"v3.0"Windows Workflow Foundation"SQL"EN"SqlPersistenceService_Logic.sql。


        创建报销审批工作流:


            1:在设计面板中拉一个WhileActivity;


            2:在代码中加入一个属性,标记while的执行条件:!this.IsCompleted
  1.         /// <summary>
  2.         /// 工作流的while条件
  3.         /// </summary>
  4.         private Boolean isCompleted = false;
  5.         public Boolean IsCompleted
  6.         {
  7.             get { return isCompleted; }
  8.             set { isCompleted = value; }
  9.         }
复制代码
3:拉一个SequenceActivity。


          4:再放一个ListenActivity,也叫单线触发容器,使用EventDrivenActivity作为分支容器,当某条分支中的结点执行完成后,该ListenActivity结点就结束,继续向下执行,其他分支内的结点就不执行了,它不能应用于状态机工作流。


          5:加入所有的EventDrivenActivity。


          6:创建一个供EventDrivenActivity用的接口IApprove
  1. /// <summary>
  2.     /// 这个接口标示为"ExternalDataExchange",目的是供工作流调用
  3.     /// </summary>
  4.     [ExternalDataExchange]
  5.   public  interface IApprove
  6.     {
  7.         /// <summary>
  8.         /// 员工提交报销记录事件
  9.         /// </summary>
  10.         event EventHandler<ExpenseAccountInfo> OnStaffSubmit;
  11.         /// <summary>
  12.         /// 员工删除报销记录事件
  13.         /// </summary>
  14.         event EventHandler<ExpenseAccountInfo> OnStaffDelete;
  15.         /// <summary>
  16.         /// 小额数据PM审批通过事件
  17.         /// </summary>
  18.         event EventHandler<ExpenseAccountInfo> OnPMSubmitMin;
  19.         /// <summary>
  20.         /// 大额数据PM审批通过事件
  21.         /// 同时提交给副总审批
  22.         /// </summary>
  23.         event EventHandler<ExpenseAccountInfo> OnPMSubmitMax;
  24.         /// <summary>
  25.         /// PM拒绝审批事件
  26.         /// </summary>
  27.         event EventHandler<ExpenseAccountInfo> OnPMReject;
  28.         /// <summary>
  29.         /// 副总审批通过事件
  30.         /// </summary>
  31.         event EventHandler<ExpenseAccountInfo> OnVPSubmit;
  32.         /// <summary>
  33.         /// 副总拒绝审批事件
  34.         /// </summary>
  35.         event EventHandler<ExpenseAccountInfo> OnVPReject;
  36.         /// <summary>
  37.         /// 财务通过审批事件
  38.         /// </summary>
  39.         event EventHandler<ExpenseAccountInfo> OnFinanceSubmit;
  40.     }
复制代码
7:设置EventDrivenActivity的属性:EventName,InterfaceType,Name,Invoked。InterfaceType选择第六步创建的接口。所有的工作流事件代码如下:

          注意: 财务审批事件(OnFinanceSubmit_Invoked)是工作流最后一步,所以工作流的完成标志也要在这完成。
  1. private void OnStaffSubmit_Invoked(object sender, ExternalDataEventArgs e)
  2.         {
  3.             BllExpense Bll = new BllExpense();
  4.             info = e as ExpenseAccountInfo;
  5.             info.AppStatus = "等待PM审批";
  6.             Bll.AddRecord(info);
  7.         }
  8.         private void OnStaffDelete_Invoked(object sender, ExternalDataEventArgs e)
  9.         {
  10.             BllExpense Bll = new BllExpense();
  11.             info.AppStatus = "结束";
  12.             Bll.UpDateRecord(info);
  13.         }
  14.         private void OnPMSubmitMin_Invoked(object sender, ExternalDataEventArgs e)
  15.         {
  16.             BllExpense Bll = new BllExpense();
  17.             info.AppStatus = "审批通过";
  18.             Bll.UpDateRecord(info);
  19.         }
  20.         private void OnPMSubmitMax_Invoked(object sender, ExternalDataEventArgs e)
  21.         {
  22.             BllExpense Bll = new BllExpense();
  23.             info.AppStatus = "等待VP审批";
  24.             Bll.UpDateRecord(info);
  25.         }
  26.         private void OnPMReject_Invoked(object sender, ExternalDataEventArgs e)
  27.         {
  28.             BllExpense Bll = new BllExpense();
  29.             info.AppStatus = "PM拒绝审批";
  30.             Bll.UpDateRecord(info);
  31.         }
  32.         private void OnVPSubmit_Invoked(object sender, ExternalDataEventArgs e)
  33.         {
  34.             BllExpense Bll = new BllExpense();
  35.             info.AppStatus = "审批通过";
  36.             Bll.UpDateRecord(info);
  37.         }
  38.         private void OnVPReject_Invoked(object sender, ExternalDataEventArgs e)
  39.         {
  40.             BllExpense Bll = new BllExpense();
  41.             info.AppStatus = "VP拒绝审批";
  42.             Bll.UpDateRecord(info);
  43.         }
  44.         private void OnFinanceSubmit_Invoked(object sender, ExternalDataEventArgs e)
  45.         {
  46.             BllExpense Bll = new BllExpense();
  47.             info.AppStatus = "结束";
  48.             Bll.UpDateRecord(info);
  49.             //结束工作流
  50.             this.IsCompleted = true;
  51.         }
复制代码
8:创建一个业务逻辑类BLL_Approve来实现IApprove。
  1. public  class BLL_Approve:IApprove
  2.     {
  3.       public event EventHandler<ExpenseAccountInfo> OnStaffSubmit;
  4.         public event EventHandler<ExpenseAccountInfo> OnStaffDelete;
  5.         public event EventHandler<ExpenseAccountInfo> OnPMSubmitMin;
  6.         public event EventHandler<ExpenseAccountInfo> OnPMSubmitMax;
  7.         public event EventHandler<ExpenseAccountInfo> OnPMReject;
  8.         public event EventHandler<ExpenseAccountInfo> OnVPSubmit;
  9.         public event EventHandler<ExpenseAccountInfo> OnVPReject;
  10.         public event EventHandler<ExpenseAccountInfo> OnFinanceSubmit;   
  11.         public void RaiseStaffSubmit(ExpenseAccountInfo info)
  12.         {
  13.             if (OnStaffSubmit != null)
  14.             {
  15.                 OnStaffSubmit(null, info);
  16.             }
  17.         }
  18.         public void RaiseStaffDelete(ExpenseAccountInfo info)
  19.         {
  20.             if (OnStaffDelete != null)
  21.             {
  22.                 OnStaffDelete(null, info);
  23.             }
  24.         }
  25.         public void RaisePMSubmitMin(ExpenseAccountInfo info)
  26.         {
  27.             if (OnPMSubmitMin != null)
  28.             {
  29.                 OnPMSubmitMin(null, info);
  30.             }
  31.         }
  32.         public void RaisePMSubmitMax(ExpenseAccountInfo info)
  33.         {
  34.             if (OnPMSubmitMax != null)
  35.             {
  36.                 OnPMSubmitMax(null, info);
  37.             }
  38.         }
  39.         public void RaisePMReject(ExpenseAccountInfo info)
  40.         {
  41.             if (OnPMReject != null)
  42.             {
  43.                 OnPMReject(null, info);
  44.             }
  45.         }
  46.         public void RaiseVPSubmit(ExpenseAccountInfo info)
  47.         {
  48.             if (OnVPSubmit != null)
  49.             {
  50.                 OnVPSubmit(null, info);
  51.             }
  52.         }
  53.         public void RaiseVPReject(ExpenseAccountInfo info)
  54.         {
  55.             if (OnVPReject != null)
  56.             {
  57.                 OnVPReject(null, info);
  58.             }
  59.         }
  60.         public void RaiseFinanceSubmit(ExpenseAccountInfo info)
  61.         {
  62.             if (OnFinanceSubmit != null)
  63.             {
  64.                 OnFinanceSubmit(null, info);
  65.             }
  66.         }
  67.     }
复制代码
9:把工作流加入到asp.net中:具体方法见代码,相关方法应用可参考MSDN。
  1. BllExpense Bll;
  2.         static WorkflowRuntime runtime;//运行时
  3.         static WorkflowInstance instance;//实例
  4.         static ExternalDataExchangeService service;//外部数据交换服务
  5.         static WorkflowPersistenceService perService;//持久化服务
  6.         static BLL_Approve project;//实现接口类
  7.         protected void Page_Load(object sender, EventArgs e)
  8.         {
  9.             Bll = new BllExpense();
  10.             if (!IsPostBack)
  11.             {
  12.                 runtime = new WorkflowRuntime();
  13.                 service = new ExternalDataExchangeService();
  14.                 project = new BLL_Approve();

  15.                 perService = new SqlWorkflowPersistenceService

  16.                 (ConfigurationManager.ConnectionStrings["perstr"].ConnectionString);



  17.                 if (runtime.GetService(service.GetType()) == null)//服务不能重复加入
  18.                 {
  19.                     runtime.AddService(service);
  20.                 }
  21.                 if (runtime.GetService(perService.GetType()) == null)
  22.                 {
  23.                     runtime.AddService(perService);
  24.                 }
  25.                 if (service.GetService(project.GetType()) == null)
  26.                 {
  27.                     service.AddService(project);//将此类加入外部数据交换服务
  28.                 }
  29.                 runtime.WorkflowIdled += OnWorkflowIdled;//工作流闲置事件
  30.                 runtime.StartRuntime();
  31.             }
  32.         }
  33. public void OnWorkflowIdled(object sender, WorkflowEventArgs e)
  34.         {
  35.             e.WorkflowInstance.TryUnload();//将内存数据持久化到数据库中
  36.         }
复制代码
10:用户提交数据启动工作流。
  1.             //创建一个工作流实例
  2.             instance = runtime.CreateWorkflow(typeof(ApproveWorkFlow.MyWorkFlow  .Workflow1 ));
  3.             //启动工作流
  4.             instance.Start();

  5.             ExpenseAccountInfo info = new ExpenseAccountInfo(instance.InstanceId,

  6. Convert.ToDecimal(this.tbMoney.Text), this.tbName.Text,

  7. DateTime.Now.ToShortDateString(), "等待PM审批", this.tbNotes.Text);

  8.             //触发工作流相应事件
  9.             project.RaiseStaffSubmit(info);
复制代码
11:审批者审批工作流。
  1.     Guid workflowId = new Guid(this.tbNo.Text);
  2.             //根据报销单号取得一个未完成的工作流实例
  3.             runtime.GetWorkflow(workflowId);

  4.             ExpenseAccountInfo info = new ExpenseAccountInfo                      (workflowId, Convert.ToDecimal(this.tbMoney.Text), this.tbName.Text,

  5. DateTime.Now.ToShortDateString(), "", this.tbNotes.Text);

  6.             if (info.Amount < 1000)
  7.             {
  8.                 info.AppStatus = "审批通过";
  9.                 //触发工作流事件
  10.                 project.RaisePMSubmitMin(info);
  11.             }
  12.             else
  13.             {
  14.                 info.AppStatus = "等待VP审批";
  15.                 //触发工作流事件
  16.                 project.RaisePMSubmitMax(info);
  17.             }
复制代码
总结:本文并非原创,是以别人代码为基础,做了些改动,让它更接近一个真实的项目。(文/ASPNET2008  出处/博客园)

本文参考:
坚持学习WF(13):WF中的持久化服务
ListenActivity

项目代码下载:
TOP