• WF4与MVC结合示例


      很多初学者,首先最想解决的问题是:如何将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()就可以了。

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

  • 相关阅读:
    php数组通过值获得键
    php 重定向
    php 数组排序
    SVN使用操作
    Java + Jsp web 项目
    create-react-app搭建React项目
    双向链表实现查询、删除、插入、末尾增加
    顺序存储结构实现查询、删除、插入、末尾增加
    单向链表实现查询、删除、插入、末尾增加
    数列
  • 原文地址:https://www.cnblogs.com/kissdodog/p/4031873.html
Copyright © 2020-2023  润新知