最近正在學習工作流,正好從網上搜尋到一個Asp.net工作流(WWF+LINQ)的例子,之前學習MOSS時接觸過工作流,不過那是針對MOSS的工作流,我一直從事B/S架構開發,知道工作流可用于很多環境,其中就包含asp.net,這讓我非常想做一個這樣的執行個體。看了它的源碼,大部分都覺的非常不錯,不過也有些個人認為不太滿意的地方,這可能是因為作者考慮到隻是一個簡單的demo,沒有必要注意一些非技術方面的細節問題。
項目介紹:一個報帳二級審批工作流,資料層操作采用LINQ TO SQL,雖然MS不再發展示它。
第一級:員工送出報帳表給PM(經理),如果數目大于1000RMB,經理如果選擇通過審批,工作流會到副總的二級審批,同時經理可以直接選擇拒絕。
第二級:PV(副總),副總收到PM的審批後,可以選擇通過或者是拒絕,如果通過,工作流會送出到财務。由财務最後結束工作流。
流程圖如下:
我建立的工作流與原作者的源碼有以下幾點不同:
1:原文的工作流采用的是狀态機工作流,而這裡我采用順序工作流。
2:把資料操作部分和業務邏輯以及頁面層功能完全分開,即,隻要是和資料通路,操作相關的代碼隻允許出現在資料處理層中,而業務邏輯層以及頁面層均不允許,頁面層隻和業務邏輯層溝通,不允許直接通路資料處理層。
3:工程的命名上有改動,例如:
解決方案名稱:ApproveWorkFlow, 頁面層叫ApproveWorkFlow.Web。
4:對相關的方法做了适當的改進。
5:增加方法以及文檔注釋。
項目結構圖如下:
項目結構說明:
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