最近正在學習工作流,正好從網上搜索到一個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
/// 工作流的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
/// 這個接口標示爲"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)是工作流最後一步,所以工作流的完成標誌也要在這完成。
{
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 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。
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:審批者審批工作流。
//根據報銷單號取得一個未完成的工作流實例
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);
}
總結:本文並非原創,是以別人代碼爲基礎,做了些改動,讓它更接近一個真實的項目。