创建一个简单的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.总结,这样一个完成的一级审批流程就完成了。剩下的工作就是画图就行了,对程序来说一级审批和二十级审批效果是一样的。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值