天天看點

轉 asp.net中的報帳多級審批工作流

   最近正在學習工作流,正好從網上搜尋到一個Asp.net工作流(WWF+LINQ)的例子,之前學習MOSS時接觸過工作流,不過那是針對MOSS的工作流,我一直從事B/S架構開發,知道工作流可用于很多環境,其中就包含asp.net,這讓我非常想做一個這樣的執行個體。看了它的源碼,大部分都覺的非常不錯,不過也有些個人認為不太滿意的地方,這可能是因為作者考慮到隻是一個簡單的demo,沒有必要注意一些非技術方面的細節問題。

       項目介紹:一個報帳二級審批工作流,資料層操作采用LINQ TO SQL,雖然MS不再發展示它。

          第一級:員工送出報帳表給PM(經理),如果數目大于1000RMB,經理如果選擇通過審批,工作流會到副總的二級審批,同時經理可以直接選擇拒絕。

          第二級:PV(副總),副總收到PM的審批後,可以選擇通過或者是拒絕,如果通過,工作流會送出到财務。由财務最後結束工作流。

       流程圖如下:

轉 asp.net中的報帳多級審批工作流

       我建立的工作流與原作者的源碼有以下幾點不同:

           1:原文的工作流采用的是狀态機工作流,而這裡我采用順序工作流。 

           2:把資料操作部分和業務邏輯以及頁面層功能完全分開,即,隻要是和資料通路,操作相關的代碼隻允許出現在資料處理層中,而業務邏輯層以及頁面層均不允許,頁面層隻和業務邏輯層溝通,不允許直接通路資料處理層。

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

                解決方案名稱:ApproveWorkFlow, 頁面層叫ApproveWorkFlow.Web。

           4:對相關的方法做了适當的改進。

           5:增加方法以及文檔注釋。

       項目結構圖如下:

轉 asp.net中的報帳多級審批工作流

       項目結構說明:

           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

         ///   <summary>

         ///  工作流的while條件

         ///   </summary>

         private  Boolean isCompleted  =   false ;

         public  Boolean IsCompleted

        {

             get  {  return  isCompleted; }

             set  { isCompleted  =  value; }

        }

          3:拉一個SequenceActivity。

          4:再放一個ListenActivity,也叫單線觸發容器,使用EventDrivenActivity作為分支容器,當某條分支中的結點執行完成後,該ListenActivity結點就結束,繼續向下執行,其他分支内的結點就不執行了,它不能應用于狀态機工作流。

          5:加入所有的EventDrivenActivity。

          6:建立一個供EventDrivenActivity用的接口IApprove

///   <summary>

     ///  這個接口标示為"ExternalDataExchange",目的是供工作流調用

     ///   </summary>

    [ExternalDataExchange]

    public    interface  IApprove

    {

         ///   <summary>

         ///  員工送出報帳記錄事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnStaffSubmit;

         ///   <summary>

         ///  員工删除報帳記錄事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnStaffDelete;

         ///   <summary>

         ///  小額資料PM審批通過事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnPMSubmitMin;

         ///   <summary>

         ///  大額資料PM審批通過事件

         ///  同時送出給副總審批

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnPMSubmitMax;

         ///   <summary>

         ///  PM拒絕審批事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnPMReject;

         ///   <summary>

         ///  副總審批通過事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnVPSubmit;

         ///   <summary>

         ///  副總拒絕審批事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnVPReject;

         ///   <summary>

         ///  财務通過審批事件

         ///   </summary>

         event  EventHandler < ExpenseAccountInfo >  OnFinanceSubmit;

    }

          7:設定EventDrivenActivity的屬性:EventName,InterfaceType,Name,Invoked。InterfaceType選擇第六步建立的接口。所有的工作流事件代碼如下:

             注意: 财務審批事件(OnFinanceSubmit_Invoked)是工作流最後一步,是以工作流的完成标志也要在這完成。

private   void  OnStaffSubmit_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info  =  e  as  ExpenseAccountInfo;

            info.AppStatus  =   " 等待PM審批 " ;

            Bll.AddRecord(info);

        }

         private   void  OnStaffDelete_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " 結束 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnPMSubmitMin_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " 審批通過 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnPMSubmitMax_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " 等待VP審批 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnPMReject_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " PM拒絕審批 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnVPSubmit_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " 審批通過 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnVPReject_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " VP拒絕審批 " ;

            Bll.UpDateRecord(info);

        }

         private   void  OnFinanceSubmit_Invoked( object  sender, ExternalDataEventArgs e)

        {

            BllExpense Bll  =   new  BllExpense();

            info.AppStatus  =   " 結束 " ;

            Bll.UpDateRecord(info);

             // 結束工作流

             this .IsCompleted  =   true ;

        }

         8:建立一個業務邏輯類BLL_Approve來實作IApprove。

public    class  BLL_Approve:IApprove

    {

        public   event  EventHandler < ExpenseAccountInfo >  OnStaffSubmit;

         public   event  EventHandler < ExpenseAccountInfo >  OnStaffDelete;

         public   event  EventHandler < ExpenseAccountInfo >  OnPMSubmitMin;

         public   event  EventHandler < ExpenseAccountInfo >  OnPMSubmitMax;

         public   event  EventHandler < ExpenseAccountInfo >  OnPMReject;

         public   event  EventHandler < ExpenseAccountInfo >  OnVPSubmit;

         public   event  EventHandler < ExpenseAccountInfo >  OnVPReject;

         public   event  EventHandler < ExpenseAccountInfo >  OnFinanceSubmit;     

         public   void  RaiseStaffSubmit(ExpenseAccountInfo info)

        {

             if  (OnStaffSubmit  !=   null )

            {

                OnStaffSubmit( null , info);

            }

        }

         public   void  RaiseStaffDelete(ExpenseAccountInfo info)

        {

             if  (OnStaffDelete  !=   null )

            {

                OnStaffDelete( null , info);

            }

        }

         public   void  RaisePMSubmitMin(ExpenseAccountInfo info)

        {

             if  (OnPMSubmitMin  !=   null )

            {

                OnPMSubmitMin( null , info);

            }

        }

         public   void  RaisePMSubmitMax(ExpenseAccountInfo info)

        {

             if  (OnPMSubmitMax  !=   null )

            {

                OnPMSubmitMax( null , info);

            }

        }

         public   void  RaisePMReject(ExpenseAccountInfo info)

        {

             if  (OnPMReject  !=   null )

            {

                OnPMReject( null , info);

            }

        }

         public   void  RaiseVPSubmit(ExpenseAccountInfo info)

        {

             if  (OnVPSubmit  !=   null )

            {

                OnVPSubmit( null , info);

            }

        }

         public   void  RaiseVPReject(ExpenseAccountInfo info)

        {

             if  (OnVPReject  !=   null )

            {

                OnVPReject( null , info);

            }

        }

         public   void  RaiseFinanceSubmit(ExpenseAccountInfo info)

        {

             if  (OnFinanceSubmit  !=   null )

            {

                OnFinanceSubmit( null , info);

            }

        }

    }

            9:把工作流加入到asp.net中:具體方法見代碼,相關方法應用可參考MSDN。

BllExpense Bll;

         static  WorkflowRuntime runtime; // 運作時

         static  WorkflowInstance instance; // 執行個體

         static  ExternalDataExchangeService service; // 外部資料交換服務

         static  WorkflowPersistenceService perService; // 持久化服務

         static  BLL_Approve project; // 實作接口類

         protected   void  Page_Load( object  sender, EventArgs e)

        {

            Bll  =   new  BllExpense();

             if  ( ! IsPostBack)

            {

                runtime  =   new  WorkflowRuntime();

                service  =   new  ExternalDataExchangeService();

                project  =   new  BLL_Approve();

                perService = new SqlWorkflowPersistenceService

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

                if  (runtime.GetService(service.GetType())  ==   null ) // 服務不能重複加入

                {

                    runtime.AddService(service);

                }

                 if  (runtime.GetService(perService.GetType())  ==   null )

                {

                    runtime.AddService(perService);

                }

                 if  (service.GetService(project.GetType())  ==   null )

                {

                    service.AddService(project); // 将此類加入外部資料交換服務

                }

                runtime.WorkflowIdled  +=  OnWorkflowIdled; // 工作流閑置事件

                runtime.StartRuntime();

            }

        }

public   void  OnWorkflowIdled( object  sender, WorkflowEventArgs e)

        {

            e.WorkflowInstance.TryUnload(); // 将記憶體資料持久化到資料庫中

        }

         10:使用者送出資料啟動工作流。

             // 建立一個工作流執行個體

            instance  =  runtime.CreateWorkflow( typeof (ApproveWorkFlow.MyWorkFlow  .Workflow1 ));

             // 啟動工作流

            instance.Start();

            ExpenseAccountInfo info = new ExpenseAccountInfo(instance.InstanceId, 

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

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

            // 觸發工作流相應事件

            project.RaiseStaffSubmit(info);

          11:審批者審批工作流。

    Guid workflowId  =   new  Guid( this .tbNo.Text);

             // 根據報帳單号取得一個未完成的工作流執行個體

            runtime.GetWorkflow(workflowId);

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

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

            if  (info.Amount  <   1000 )

            {

                info.AppStatus  =   " 審批通過 " ;

                 // 觸發工作流事件

                project.RaisePMSubmitMin(info);

            }

             else

            {

                info.AppStatus  =   " 等待VP審批 " ;

                 // 觸發工作流事件

                project.RaisePMSubmitMax(info);

            }

       總結:本文并非原創,是以别人代碼為基礎,做了些改動,讓它更接近一個真實的項目。

原項目位址:http://download.csdn.net/down/948601/oxch2008

本文參考:1:http://www.cnblogs.com/foundation/articles/529793.html

             2:http://www.cnblogs.com/carysun/archive/2008/06/25/Persistence.html