创建一个简单的workflow工作流(WF4)
1.初始化数据库
在C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en目录下查找SqlWorkflowInstanceStoreSchema.sql和SqlWorkflowInstanceStoreLogic.sql这两个文件,在希望持久化的数据库里先执行SqlWorkflowInstanceStoreSchema.sql再执行,这样数据库初始化就完成了。
2.引用System.Activityes,System.Activities.DurableInstanceing,System.Runtime.DurableInstancing 三个dll
并且在操作workflow的类里引用,using System.Activities.DurableInstancing;在不引用System.Activityies.DurableInstanceing的时候一样可以引用,但是必须引用了这个dll,SqlWorkflowInstanceStore类才有效。
3.1创建WorkflowHelp类。
这个类主要用来操作Workflow,如持久化和加载已经持久化的记录。
首先声明
private static SqlWorkflowInstanceStore _instanceStore;
public static SqlWorkflowInstanceStore InstanceStore
{
get
{
if (_instanceStore == null)
{
_instanceStore = new SqlWorkflowInstanceStore(connString);
}
return _instanceStore;
}
}
定义一个SqlWorkflowInstanceStore实例,这个是类是为了持久化的
3.2.持久化实例
当提交一个申请的时候需要把该记录持久化到数据库
InstanceView view = InstanceStore.Execute(InstanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
Execute是异步持久化到数据库
第一个参数是实例句柄,第二个参数是需要执行的命令,第三个参数是持久化数据库超时时间,
返回值为InstanceView视图,包括持久化的数据的实例数据(摘自MSDN)
InstanceStore.DefaultInstanceOwner = view.InstanceOwner;
设置实例的默认所有者,
SqlWorkflowInstanceStore默认只使用单一的WorkflowApplication.当使用多个工作流的时候会发生一个异常,
SqlWorkflowInstanceStore does not support creating more than one lock owner concurrently. Consider setting InstanceStore.DefaultInstanceOwner to share the store among many applications.
当设置这个属性的时候不会有这个问题了。
如果确认只有一个工作流时也必须把DefaultInstanceOwner设置为Null,否则垃圾回收器可能不会回收这块内存。(MSND说的是可能)
注意 如果这个属性不设置则不可以持久化,如果设置了则不可以用同一个SqlWorkflowInstanceStore来创建,也就是说
public static SqlWorkflowInstanceStore InstanceStore
{
get
{
if (_instanceStore == null)
{
_instanceStore = new SqlWorkflowInstanceStore(connString);
}
return _instanceStore;
}
}
这句话是不完全对的,只有在application.Run()后,加上InstanceStoreObj = null,下次创建的时候重新实例化才可以连续创建,原因不明,有时间再看。
IDictionary<string, object> input = new Dictionary<string, object>
{
{ "SubmitInValue" , approveInfo }
};
WorkflowApplication application = new WorkflowApplication(new AuthFlow(), input);
为工作流的单个实力提供宿主。其中AuthFlow是画的工作流图,input是输入参数,如果不需要参数则也可以不用input.
关于input参数
对于自定义的控件,可能需要里面执行一些逻辑,如把一些页面参数存储进数据库,这个时候在初始化宿主的时候就需要把参数传进数据库。
首先需要在AuthFlow页面定义一个变量“SubmitInValue”,类型为“ApproveInfo”也就是需要更新到数据库里的实体,
在定义控件的时候声明一个 public InArgument<ApproveInfo> AssignedTo { get; set; } 属性,在AuthFolwo图上选择CreateApply在属性里就可以看到AssingedTo属性,把属性的值设置为AuthFlow页面上刚才定义的"SubmitInValue",这样在程序执行到这里的时候就会把页面的input变量传到控件里。input的第一个参数必须是在AuthFlow里定义的那个,否则无法找到。
application.InstanceStore = InstanceStore;
设置一个对象,提供ApplicationWorkf对工作流应用程序状态的访问(MSDN)
application.PersistableIdle = (e) =>
{
return PersistableIdleAction.Unload;
};
当工作流处于空闲状态时触发的动作,PersistableIdleAction的成员,None不做任何动作,Unload保持并卸载,Persist保持
application.Run()
开始工作流
3.3 加载已经持久化的工作流
public static void RunWorkflow(AuthInfo authInfo)
{
WorkflowApplication appliacation = new WorkflowApplication(new AuthFlow());
appliacation.InstanceStore = InstanceStoreObj;
appliacation.Load(authInfo.InstanceId);
appliacation.ResumeBookmark("WaitForManager", authInfo);
appliacation.PersistableIdle = (e) =>
{
return PersistableIdleAction.Unload;
};
}
首先定义一个WorkflowApplication,实例化为AuthFlow.
根据InstanceId加载工作流,重新打开标签, WaitFormanager,这个是等待经理审批的标签,这个名字是在Authfolw工作流页面定义好的,Authinfo是审批的消息实体,是标签控件需要的参数。
在Runworkflow必须加上这句话
appliacation.PersistableIdle = (e) =>
{
return PersistableIdleAction.Unload;
};
在工作流空闲的时候保存并卸载工作流
否则当前工作流会被示例永远锁定
4.1创建一个开始流程的控件。
新建-类,创建CrateApply.cs,
using System.Activities;继承自CodeActivity。
在里面定义一个属性public InArgument<ApproveInfo> AssignedTo { get; set; }
这个属性的作用是把告诉程序传入的参数是声明类型,方便存入数据。
同时需要在AutoFlow页面上定义一个输入变量SubmitInValue,记录的时候前面传入的参数input
在AuthFlow页面上点击CreateApply这个控件,把AssingedTo属性设置为SubmitInValue,这样在CreateWorkflow时候传入的参数就可以进入到CreateApply控件了。
重新类的Execute事件
protected override void Execute(CodeActivityContext context)
{
ApproveInfo approveInfo = new ApproveInfo();
string sql = " insert into Approve(InstranceId,ApproveUser,SafeLevel,Price,ApplyDate,State)";
sql += "values(";
sql += "'"+context.WorkflowInstanceId+"',";
sql += "'" + AssignedTo.Get(context).ApproveUser + "',";
sql += "'" + AssignedTo.Get(context).SafeLevel + "',";
sql += AssignedTo.Get(context).Price + ",";
sql += "'" + DateTime.Now+ "',";
sql += "'false'";
sql += ")";
SqlHelp.ExecuteNonQuery(sql);
ApproveInfo approveInfo = new ApproveInfo();
object obj = SqlHelp.ExecuteScalar(sql);
if (obj != null)
{
approveInfo.Id = Convert.ToInt32(obj);
}
approveInfo.InstranceId = context.WorkflowInstanceId;
approveInfo.ApproveUser = AssignedTo.Get(context).ApproveUser;
approveInfo.SafeLevel = AssignedTo.Get(context).SafeLevel;
approveInfo.Price= AssignedTo.Get(context).Price;
approveInfo.ApplyDate = DateTime.Now;
approveInfo.State = false;
//这句话为了将Id赋值给传入参数
OutParame.Set(context, approveInfo);
}
这里AssingedTo就可以把传入参数解析出来,存入数据库。
再在类里定义一个
public OutArgument<ApproveInfo> OutParame
{
get;
set;
}
这个参数是把修改后的数据传出去,如插入审批记录控件需要ApplyId这个参数,这个参数只可以在这里取到,所以需要把这个字段通过 OutParame.Set(context, approveInfo);传到外面。
当然如果不需要控件内的数据也可以不定义这个变量,如4.2的报销字段赋值,直接用程序开始传入的参数即可。
在AutoFlow页面上定义一个ApplyInfo类型的输出参数OutValue,选择CreateApply控件,把OutParame赋值为OutValue,这样就把控件内的数据传入到工作流中了。
这样最简单的一个控件就完成了。
4.2如何给报销费用字段赋值。
如果是1000以下经理审批,1000-2000经理,部门经理审批,2000以上经理,部门经理,总经理审批
需要在AuthFlow页面定义一个变量AuthLevel,然后放一个switch控件,但1的时候二级审批,2的时候二级审批,3的时候3级审批
为了给这个AuthLevel变量赋值,需要在CreateApply和Switch之间放一个Assign控件,通过这个控件可以把传入的SubmitInValue中的AuthLevel赋值给AuthLevel,这样switch控件就可以根据不同的值进行不同的审批级别。
4.3根据报销费用选择不同的审批流程
在Assign后放一个Switch控件,让它的泛型类型为Int32,Expression选择变量为AuthLevel.在里面增加3个case,选择分别为1,2,3。1 的时候选择1个UpdateApply控件,2的时候选择2个UpdateApply控件,3放入3个UpdateApply控件,
4.4 以1级审批为例
插入审批记录控件AuthApply控件。
这个控件的作用是在审批详细表里插入一条记录,记录审批人,审批时间,审批状态等。
创建AuthApply.cs
using System.Activities;继承自CodeActivity。
首先创建一个ExecuteRoleName属性,记录该由谁来审批
public string ExecuteRoleName
{
get;
set;
}
这样控件就可以做成公共的了,经理审批,部门经理审批,只要把RoleName修改就可以了。
定义一个输入参数public InArgument<ApproveInfo> AssignedTo { get; set; }
在AutoFlow页面上选择AuthApply控件,把AssingedTo的属性赋值为4.1的传出参数OutValue,这样就把外面传入的参数传到控件。
重写Execute方法把审批记录插入到数据库
protected override void Execute(CodeActivityContext context)
{
string sql = " insert into ApplyDetail(ApplyId,ExcuteAuthRoleId,ExcuteAuthRoleName,AuthState,CreateDate)";
//sql += "values(" + ApplyId.Get(context) + ",0,'" + ExecuteRoleName + "','" + DateTime.Now + "'";
sql += "values(" + AssignedTo.Get(context).Id + ",0,'" + ExecuteRoleName + "','false','" + DateTime.Now + "'";
sql += ")";
SqlHelp.ExecuteNonQuery(sql);
}
这样一个插入审批记录的控件就完成了。
4.5生成标签等待控件。
这个控件的作用是让工作流到这里等待用户的输入才继续向下流。
创建一个不可继承的类WaitForInput<T>,
using System.Activities;继承自NativeActivity<T>
定义一个属性
public string BookmarkName { get; set; }
因为一个流程需要很多标签,所以需要给每个标签起名字,这样在重启标签的时候才不会出错。
定义一个输出参数public OutArgument<T> Input { get; set; },把外面传入的参数传入工作流。
在AutoFlow页面上放入一个WaitForInput控件,类型为AuthInfo类型。定义一个AuthValue,类型为AuthInfo。然后选择WaitForInput控件的Input属性设置为AuthValue
这样在重启标签时传入参数就可以进入工作流了。如3.3。
重写
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(BookmarkName,
new BookmarkCallback(this.Continue));
}
void Continue(NativeActivityContext context, Bookmark bookmark,
object obj)
{
Input.Set(context, (T)obj);
}
protected override bool CanInduceIdle { get { return true; } }
泛型根据传入的参数把传入的审批信息传入到工作流。
4.6 在AutoFlow放入一个if控件,条件是刚才从等待控件传出来的值AuthValue.AuthState.如果为True则审批通过,否则拒绝审批,工作流停止。
在if控件的True条件里放入一个流控件,在流控件里放入一个AgreeAuth同意审批控件
在if控件的False条件里放入一个disAgreeAuth控件,拒绝审批,终止流程。
4.7 同意审批控件。
定义类AgreeAuth
using System.Activities;继承自CodeActivity。
在里面定义一个输入属性
public InArgument<AuthInfo> AuthInfo
{
get;
set;
}
这个参数记录的是审批信息。
在AutoFlow页面上选择AgreeAuth控件,在属性AuthInfo选项里赋值为刚才等待控件传出的值AuthValue.这样在重启标签时传入的参数就传入了控件
重新写Execute方法
protected override void Execute(CodeActivityContext context)
{
string sql = " update ApplyDetail set Author ='"+AuthInfo.Get(context).Author+"'";
sql += ", AuthState ='true',AuthorRemark='"+AuthInfo.Get(context).AuthorRemark+"'";
sql += " ,AuthorDate ='"+AuthInfo.Get(context).AuthorDate+"'";
sql += " where id="+ AuthInfo.Get(context).Id;
SqlHelp.ExecuteNonQuery(sql);
}
更新审批记录。
4.8 完成审批控件
创建CompleteFlow类
using System.Activities;继承自CodeActivity。
定义一个属性
public InArgument<AuthInfo> AuthInfo
{
get;
set;
}
这个参数记录的是审批信息。
在AutoFlow的最后加一个完成控件。把AuthInfo属性赋值为AuthValue,
重写Execute方法
protected override void Execute(CodeActivityContext context)
{
string sql = " update Approve set State ='true'";
sql += " where id=" + AuthInfo.Get(context).ApplyId;
SqlHelp.ExecuteNonQuery(sql);
}
5.总结,这样一个完成的一级审批流程就完成了。剩下的工作就是画图就行了,对程序来说一级审批和二十级审批效果是一样的。
创建一个简单的workflow工作流(WF4)相关推荐
- WF4.0入门系列1——创建一个简单的工作流
WF4.0入门系列1--创建一个简单的工作流 打开VS2010,选择文件-新建-项目,选择Workflow项 工作流台应用程序,在名称处输入chapter01,选择合适的位置,这里默认,单击确定. V ...
- Beginning WF4读书笔记(一):创建一个简单的工作流
让我们以创建一个简单的工作流开始.开启Visual Studio (VS) 2010,选择New Project.在已经安装的模版下面,选择Visual C#-Workflow,你会看到提供了四个模版 ...
- 创建一个简单的存储过程(RroGetA_Z),要求输出A到Z之间的26个大写字母
<SQL Server数据库设计与项目实践> ISBN:978-7-302-40610-5 p121 动手实践-实训内容-(1) (1)创建一个简单的存储过程(RroGetA_Z),要求输 ...
- Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏
Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏 即使是现在,很多初学游戏开发的同学,在谈到Unity的时候,依然会认为Unity只能用于制作3D游戏的.实际上,Unity在2013年发布 ...
- Linux Namespace系列(09):利用Namespace创建一个简单可用的容器
本文将演示如何利用namespace创建一个完整的容器,并在里面运行busybox.如果对namespace不是很熟悉,请先参考前面几遍介绍不同类型namespace的文章. busybox是一个Li ...
- Windows下编译TensorFlow1.3 C++ library及创建一个简单的TensorFlow C++程序
参考:https://www.cnblogs.com/jliangqiu2016/p/7642471.html Windows下编译TensorFlow1.3 C++ library及创建一个简单的T ...
- visjs使用小记-1.创建一个简单的网络拓扑图
1.插件官网:http://visjs.org/ 2.创建一个简单的网络拓扑图 <!doctype html> <html> <head><title> ...
- idea建立一个java工程_IntelliJ IDEA(三、各种工程的创建 -- 之一 -- 创建一个简单的Java工程)...
一.创建一个简单的Java工程:HelloWorld 1. Eclipse的第一步是选择工作空间,然后创建项目: IDEA不同(没有工作空间的概念),第一步就直接创建具体的项目,项目创建过程中会选择在 ...
- 使用timer控件创建一个简单的报警程序
简介: 当我使用计算机工作时,我总是如此的专心致志,以至于每当我过了"一会儿"去看时间时,发现已经过了三个小时,而我却完全没有意识到!所以我决定使用我从Code Project学来 ...
最新文章
- 软件测试培训教程:pytest与unittest区别
- PCL安装与环境变量配置(Win10)
- git bash 操作文件及文件夹命令
- 如何在Linux中安装和使用Silver Searcher(程序员的代码搜索工具)
- CompletableFuture并行异步处理类使用示例
- 影响中国历史的十篇政治美文
- 自己虚拟服务器都用json可以吗,vue+webpack项目中使用dev-server搭建虚拟服务器,请求json文件数据,实现前后台分离开发...
- 二、Nginx 反向代理配置初学个人理解
- 基于springboot的疫情网课教学平台
- mysql cmd定时_windows下定时执行mysql冷备份
- Latex 版本简历
- 无法实现的梦想:孤独之旅计划
- 基于python微信群聊机器人开题报告
- 面试海量数据处理题总结
- Token是什么 Token登录认证
- 杜红超、彭志红担任BCF理事
- JZOJ 6310.glo【LIS】【线段树】
- 没做过项目经理可以考pmp证书吗?普通人考PMP®有用吗?
- 全球经典设计风格之孟菲斯设计
- SQLite实现获取本机短信数据
热门文章
- 日本使用ips细胞制作“类器官”的最新进展
- android蓝牙传输文件到mysql_蓝牙opp文件发送过程剖析
- 导数的奇偶性(含证明)
- 素数总结(包含素数表)
- 导入式样式表CSS与链接式样式表CSS的区别
- 如何用MATLAB代码求解偏微分方程组
- 原码、补数、补码以及计算机中为什么用补码存储
- 2022-2028年中国银行IT行业市场发展前景及投资风险评估报告
- C语言学生管理系统项目
- R语言ggplot2可视化:patchwork包(直接使用加号+)将一个ggplot2可视化结果和一段文本内容横向组合起来形成最终结果图、使用wrap_elements函数将文本内容放置在组合组左边