C军

不玩博客了!

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  很多初学者,首先最想解决的问题是:如何将WF与MVC程序相结合。由于Web程序属于长时间运行的流程,因此持续化功能的运用就非常重要了。

  本文将结合书签、WorkflowApplication、生命周期事件、MVC、持续化、传参、状态机实现一个简单的审核流程的示例。

  本文模拟一个用户注册流程,此流程非常简单,简单到什么地步?

  两个用户,版主与管理员,版主负责帮助录入新用户信息,但需要管理员审核通过后才插入数据库,否则审核不通过回退给版主修改。

  首先设计一张表如下:

  

  真实环境中不应该这样设计,而是外键关联用户数据(Id、Name、PassWord、Age),本处作为Demo,一切从简。

  首先设计一个流程如下:

  

  流程并不复杂,此处需要3个"活动类","一个书签类"。如"提交"活动类代码:

    public sealed class Upload : CodeActivity
    {
        public InOutArgument<WorkflowModel> workflowModel { get; set; }
        protected override void Execute(CodeActivityContext context)
        {
            WorkflowModel model = context.GetValue<WorkflowModel>(workflowModel);
            model.WorkflowId = context.WorkflowInstanceId;
            model.State = "等待审核";
            try
            {
                model.Add();    //插入数据库
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }

 

  其放在"提交"State里。

  

 

  公用书签代码如下:

    public sealed class CustomBookmark : NativeActivity
    {
        public InArgument<string> BMName { get; set; }
        public InOutArgument<WorkflowModel> workflowModel { get; set; }
        protected override bool CanInduceIdle { get { return true; } }
        protected override void Execute(NativeActivityContext context)
        {
            string BookMarkName = context.GetValue<string>(BMName);
            context.CreateBookmark(BookMarkName, new BookmarkCallback(borkmarkCallback));
        }
        void borkmarkCallback(NativeActivityContext context, Bookmark bookmark, object obj)
        {
            Dictionary<string, object> Dic = obj as Dictionary<string, object>;
            context.SetValue(workflowModel, Dic["Model"] as WorkflowModel);
        }
    }

 

  其放在“状态切换线”上,如回退书签:

  

  整个工作流仅仅一个参数,上面那张表的一个实体:

  

  此参数,也作为全局变量使用了。

  如果在MVC的Controller里面的Action里面操作工作流的话,代码会非常的乱。所以另外起了一个工作流帮助类。

  核心类

  其代码如下:

  public class WorkflowHelper
    {
        #region 工作流属性

        WorkflowApplication instance = null;
        SqlWorkflowInstanceStore instanceStore = null;
        InstanceView view;
        AutoResetEvent idleEvent = new AutoResetEvent(false);

        #endregion

        //初始化工作流
        public void InitialWorkflowApplication()
        {
            string connectionString = "server=192.168.0.69;database=waterageII;uid=water;pwd=water";
            instance.Idle = delegate(WorkflowApplicationIdleEventArgs e)
            {
                idleEvent.Set();
            };
            instance.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
            {
                idleEvent.Set();
            };
            instanceStore = new SqlWorkflowInstanceStore(connectionString);
            view = instanceStore.Execute(instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
            instanceStore.DefaultInstanceOwner = view.InstanceOwner;
            instance.InstanceStore = instanceStore;
        }
 
        public string Next(WorkflowModel model)
        {
            if (model.Id <= 0)
            {
                Dictionary<string, object> Dic = new Dictionary<string, object>();
                Dic.Add("Model", model);
                instance = new WorkflowApplication(new Register(), Dic);
                InitialWorkflowApplication();
                instance.Run();
            }
            else
            {
                instance = new WorkflowApplication(new Register());
                InitialWorkflowApplication();
                instance.Load(model.WorkflowId);
                if (instance.GetBookmarks().Count() > 0)
                {
                    Dictionary<string, object> Dic = new Dictionary<string, object>();
                    Dic.Add("Model", model);
                    BookmarkResumptionResult BRR = instance.ResumeBookmark("Bookmark", Dic);
                }
            }
            //等待工作流线程空闲,才往下走
            idleEvent.WaitOne();
            instance.Unload();
            return "运行成功";
        }
        public WorkflowModel GetModelById(int Id)
        {
            WorkflowModel w = new WorkflowModel();
            DataTable dt = w.Get(Id);
            WorkflowModel model = new WorkflowModel();
            if (dt != null)
            {
                DataRow dr = dt.Rows[0];
                model.Id = Convert.ToInt32(dr["Id"]);
                model.Name = Convert.ToString(dr["Name"]);
                model.PassWord = Convert.ToString(dr["PassWord"]);
                model.Age = Convert.ToInt32(dr["Age"]);
                model.State = Convert.ToString(dr["State"]);
                model.BelongUser = Convert.ToString(dr["BelongUser"]);
                model.OperateUser = Convert.ToString(dr["OperateUser"]);
                model.OperateTime = Convert.ToDateTime(dr["OperateTime"]);
                model.OperateType = Convert.ToString(dr["OperateType"]);
                model.WorkflowId = new Guid(Convert.ToString(dr["WorkflowId"]));
                return model;
            }
            return null;
        }
    }

 

   这样一封装了之后,MVC的Controller非常简洁:

    public class HomeController : Controller
    {
        User Manager = new User() { Name = "管理员" };
        User NewUser = new User() { Name = "版主" };

        [HttpGet]
        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Login(string UserName)
        {
            Session["UserName"] = UserName;
            return Redirect("/Home/List");
        }

        public ActionResult List()
        {
            //管理员列表,能看到"等待审核"的数据,而版主能看到"审核回退"的数据
            string State = Session["UserName"].ToString() == "管理员" ? "等待审核" : "审核回退";
            WorkflowModel model = new WorkflowModel();
            DataTable dt = model.GetNewestByState(State);
            return View(dt);
        }

        public ActionResult Register()
        {
            return View();
        }

        public ActionResult Upload(WorkflowModel model)
        {
            model.OperateUser = Session["UserName"].ToString();
            model.OperateTime = DateTime.Now;
            model.OperateType = "提交";

            WorkflowHelper WFController = new WorkflowHelper();
            WFController.Next(model);

            return Content("提交成功,请等待管理员审核!");
        }

        [HttpGet]
        public ActionResult Check(int Id)
        {
            WorkflowHelper WFController = new WorkflowHelper();
            WorkflowModel model = WFController.GetModelById(Id);
            return View(model);
        }

        [HttpPost]
        public ActionResult Check(WorkflowModel model)
        {
            model.OperateUser = Session["UserName"].ToString();
            model.OperateTime = DateTime.Now;
            model.OperateType = "审核";

            //审核(调用控制器的审核方法)
            WorkflowHelper WFController = new WorkflowHelper();
            string response = WFController.Next(model);

            return Content(response);
        }

        public ActionResult Return(int Id)
        {
            WorkflowHelper WFController = new WorkflowHelper();
            WorkflowModel model = WFController.GetModelById(Id);
            return View(model);
        }
    }

  Controller就只做一件事,拿数据,调用流程控制器的方法(无论当前是在哪一步,都只管调用Helper类的Next()方法,我用了多个页面,相对来说,一个页面一个比较好,因为会涉及到权限以及下一步的角色与用户),所有流程的初始化,流程走到哪一步了,是否已经持续化,能不能加载出来,在Controller里面都不用管。

  实体层(在里面包含了数据库访问)如下:

public class WorkflowModel
    {
        static string ConstringSql = "server=CZZ;database=xxoo;uid=sa;pwd=123;";
        public int Id { get; set; }
        public string Name { get; set; }
        public string PassWord { get; set; }
        public int Age { get; set; }
        public string Option { get; set; }
        public string State { get; set; }
        public string BelongUser { get; set; }
        public string OperateUser { get; set; }
        public DateTime OperateTime { get; set; }
        public string OperateType { get; set; }
        public Guid WorkflowId { get; set; }

        public void Add()
        {
            SqlConnection conn = new SqlConnection(ConstringSql);        
            SqlCommand cmd = conn.CreateCommand();              
            cmd.CommandText = "INSERT INTO WorkflowRegister VALUES(@Name,@PassWord,@Age,@Option,@State,@BelongUser,@OperateUser,@OperateTime,@OperateType,@WorkflowId)";
            //设置参数类型
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar);
            cmd.Parameters.Add("@PassWord", SqlDbType.VarChar);
            cmd.Parameters.Add("@Age", SqlDbType.Int);
            cmd.Parameters.Add("@Option", SqlDbType.NVarChar);
            cmd.Parameters.Add("@State", SqlDbType.NVarChar);
            cmd.Parameters.Add("@BelongUser", SqlDbType.NVarChar);
            cmd.Parameters.Add("@OperateUser", SqlDbType.NVarChar);
            cmd.Parameters.Add("@OperateTime", SqlDbType.DateTime);
            cmd.Parameters.Add("@OperateType", SqlDbType.NVarChar);
            cmd.Parameters.Add("@WorkflowId", SqlDbType.UniqueIdentifier);
            //设置参数值
            cmd.Parameters["@Name"].Value = this.Name == null ? "" : this.Name;
            cmd.Parameters["@PassWord"].Value = this.PassWord == null ? "" : this.PassWord;
            cmd.Parameters["@Age"].Value = this.Age;
            cmd.Parameters["@Option"].Value = this.Option == null ? "" : this.Option;
            cmd.Parameters["@State"].Value = this.State == null ? "" : this.State;
            cmd.Parameters["@BelongUser"].Value = this.BelongUser == null ? "" : this.BelongUser;
            cmd.Parameters["@OperateUser"].Value = this.OperateUser == null ? "" : this.OperateUser;
            cmd.Parameters["@OperateTime"].Value = this.OperateTime;
            cmd.Parameters["@OperateType"].Value = this.OperateType == null ? "" : this.OperateType;
            cmd.Parameters["@WorkflowId"].Value = this.WorkflowId;
            conn.Open();
            cmd.ExecuteNonQuery();
            conn.Close();
        }

        public DataTable Get(int Id)
        {
            SqlConnection conn = new SqlConnection(ConstringSql);
            string strSql = string.Format("SELECT * FROM WorkflowRegister WHERE Id = {0}", Id);
            DataTable dt = new DataTable();
            SqlDataAdapter da = new SqlDataAdapter(strSql, conn);
            da.Fill(dt);
            if (dt.Rows.Count > 0)
            {
                return dt;
            }
            return null;
        }

        public DataTable GetNewestByState(string State)
        {
            SqlConnection conn = new SqlConnection(ConstringSql);
            string strSql = string.Format("SELECT W1.* FROM WorkflowRegister AS W1 LEFT JOIN WorkflowRegister AS W2 ON W1.WorkflowId = W2.WorkflowId AND W1.OperateTime < W2.OperateTime WHERE W2.Id IS NULL AND W1.State = '{0}'", State);
            DataTable dt = new DataTable();
            SqlDataAdapter da = new SqlDataAdapter(strSql, conn);
            da.Fill(dt);
            if (dt.Rows.Count > 0)
            {
                return dt;
            }
            return null;
        }
    }

  代码贴得差不多了,来看看运行效果:

  版主能够看到状态为"审核回退"案件列表:

  

  管理员能够看到状态为"等待审核"的列表:

  

  提交页面如下:

  

  当一条流程完整走下来,数据库信息大致如下:

  

  WF如果运用得当,应该能够帮助自己更好地理清流程走向。对于工作流之外的开发者来说,就只管Next(),Next()就可以了。

  下一篇文章应该是考虑,如果将下一步的角色、选择用户的功能等内容也封装进入流程。

posted on 2014-10-17 19:10  逆心  阅读(1123)  评论(2编辑  收藏  举报