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)相关推荐

  1. WF4.0入门系列1——创建一个简单的工作流

    WF4.0入门系列1--创建一个简单的工作流 打开VS2010,选择文件-新建-项目,选择Workflow项 工作流台应用程序,在名称处输入chapter01,选择合适的位置,这里默认,单击确定. V ...

  2. Beginning WF4读书笔记(一):创建一个简单的工作流

    让我们以创建一个简单的工作流开始.开启Visual Studio (VS) 2010,选择New Project.在已经安装的模版下面,选择Visual C#-Workflow,你会看到提供了四个模版 ...

  3. 创建一个简单的存储过程(RroGetA_Z),要求输出A到Z之间的26个大写字母

    <SQL Server数据库设计与项目实践> ISBN:978-7-302-40610-5 p121 动手实践-实训内容-(1) (1)创建一个简单的存储过程(RroGetA_Z),要求输 ...

  4. Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏 即使是现在,很多初学游戏开发的同学,在谈到Unity的时候,依然会认为Unity只能用于制作3D游戏的.实际上,Unity在2013年发布 ...

  5. Linux Namespace系列(09):利用Namespace创建一个简单可用的容器

    本文将演示如何利用namespace创建一个完整的容器,并在里面运行busybox.如果对namespace不是很熟悉,请先参考前面几遍介绍不同类型namespace的文章. busybox是一个Li ...

  6. Windows下编译TensorFlow1.3 C++ library及创建一个简单的TensorFlow C++程序

    参考:https://www.cnblogs.com/jliangqiu2016/p/7642471.html Windows下编译TensorFlow1.3 C++ library及创建一个简单的T ...

  7. visjs使用小记-1.创建一个简单的网络拓扑图

    1.插件官网:http://visjs.org/  2.创建一个简单的网络拓扑图 <!doctype html> <html> <head><title> ...

  8. idea建立一个java工程_IntelliJ IDEA(三、各种工程的创建 -- 之一 -- 创建一个简单的Java工程)...

    一.创建一个简单的Java工程:HelloWorld 1. Eclipse的第一步是选择工作空间,然后创建项目: IDEA不同(没有工作空间的概念),第一步就直接创建具体的项目,项目创建过程中会选择在 ...

  9. 使用timer控件创建一个简单的报警程序

    简介: 当我使用计算机工作时,我总是如此的专心致志,以至于每当我过了"一会儿"去看时间时,发现已经过了三个小时,而我却完全没有意识到!所以我决定使用我从Code Project学来 ...

最新文章

  1. 软件测试培训教程:pytest与unittest区别
  2. PCL安装与环境变量配置(Win10)
  3. git bash 操作文件及文件夹命令
  4. 如何在Linux中安装和使用Silver Searcher(程序员的代码搜索工具)
  5. CompletableFuture并行异步处理类使用示例
  6. 影响中国历史的十篇政治美文
  7. 自己虚拟服务器都用json可以吗,vue+webpack项目中使用dev-server搭建虚拟服务器,请求json文件数据,实现前后台分离开发...
  8. 二、Nginx 反向代理配置初学个人理解
  9. 基于springboot的疫情网课教学平台
  10. mysql cmd定时_windows下定时执行mysql冷备份
  11. Latex 版本简历
  12. 无法实现的梦想:孤独之旅计划
  13. 基于python微信群聊机器人开题报告
  14. 面试海量数据处理题总结
  15. Token是什么 Token登录认证
  16. 杜红超、彭志红担任BCF理事
  17. JZOJ 6310.glo【LIS】【线段树】
  18. 没做过项目经理可以考pmp证书吗?普通人考PMP®有用吗?
  19. 全球经典设计风格之孟菲斯设计
  20. SQLite实现获取本机短信数据

热门文章

  1. 日本使用ips细胞制作“类器官”的最新进展
  2. android蓝牙传输文件到mysql_蓝牙opp文件发送过程剖析
  3. 导数的奇偶性(含证明)
  4. 素数总结(包含素数表)
  5. 导入式样式表CSS与链接式样式表CSS的区别
  6. 如何用MATLAB代码求解偏微分方程组
  7. 原码、补数、补码以及计算机中为什么用补码存储
  8. 2022-2028年中国银行IT行业市场发展前景及投资风险评估报告
  9. C语言学生管理系统项目
  10. R语言ggplot2可视化:patchwork包(直接使用加号+)将一个ggplot2可视化结果和一段文本内容横向组合起来形成最终结果图、使用wrap_elements函数将文本内容放置在组合组左边