很多初学者,首先最想解决的问题是:如何将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());
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
instance.InstanceStore = instanceStore;
} public string Next(WorkflowModel model)
{
if (model.Id <= )
{
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() > )
{
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[];
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 > )
{
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 > )
{
return dt;
}
return null;
}
}

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

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

  

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

  

  提交页面如下:

  

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

  

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

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

WF4与MVC结合示例的更多相关文章

  1. Spring MVC 项目示例

    Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架.Spring的web框架围绕DispatcherServlet设计, 作用是将请求分发到不同 ...

  2. 1.【转】spring MVC入门示例(hello world demo)

    1. Spring MVC介绍 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于 ...

  3. Spring MVC 入门示例讲解

    在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序.Spring MVC 是Spring框架最重要的的模块之一.它以强大的Spring IoC容器为基础,并充分利用容器的特性来简 ...

  4. Spring3 MVC入门示例

    Spring3 MVC 介绍: 1. Spring MVC 是Spring 框架的Web组件,能够开发WEB工程 2. 能与其它框架(Struts2)很好的集成 3.  Spring MVC 是以se ...

  5. Mvc 自带分页控件PagedList.Mvc Demo示例

    添加/下载PagedList.Mvc 直接搜索mvc pagelist 就会出来.安装完成即可.在项目的packages文件夹下面就会出现PagedList.Mvc.4.5.0.0 和PagedLis ...

  6. Spring MVC 完整示例

    在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序.Spring MVC 是Spring框架最重要的的模块之一.它以强大的Spring IoC容器为基础,并充分利用容器的特性来简 ...

  7. Spring MVC 入门示例讲解 - howtodoinjava

    在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序.Spring MVC 是Spring框架最重要的的模块之一.它以强大的Spring IoC容器为基础,并充分利用容器的特性来简 ...

  8. IDEA+Maven+Spring MVC HelloWorld示例

    用Maven创建Web项目 选择webapp模板 创建成功后点Enable Auto-Import idea给我们创建出来的结构是这样的,这还不标准,需要自己修改. 在main文件夹下创建java文件 ...

  9. 【转】spring MVC入门示例(hello world demo)

    部分内容来自网络:<第二章 Spring MVC入门 —— 跟开涛学SpringMVC > 1. Spring MVC介绍 Spring Web MVC是一种基于Java的实现了Web M ...

随机推荐

  1. [转]VGA、QVGA、CIF、QCIF 。。。的含义

    转自:http://www.360doc.com/content/07/0329/15/494_419655.shtml 相信大家时常都听到手机支持 VGA 镜头.QVGA 屏幕显示.CIF 短片拍摄 ...

  2. ylbtech-dbs:ylbtech-4,PurpleHouse(房地产楼盘销售系统)

    ylbtech-dbs:ylbtech-4,PurpleHouse(房地产楼盘销售系统) -- =============================================-- Crea ...

  3. 编译器错误消息: CS0234: 命名空间“Purple”中不存在类型或命名空间名称“Model”(是否缺少程序集引用?)

    编译错误 “/storeimg”应用程序中的服务器错误. 编译错误 说明: 在编译向该请求提供服务所需资源的过程中出现错误.请检查下列特定错误详细信息并适当地修改源代码. 编译器错误消息: CS023 ...

  4. Oracle迁移MySQL笔记

    1,--在oracle代表注释 ,mysql/* */,# 2,|| oracle里面是表示连接符号,比如 A||B 那么就是AB 3,databaseLink创建好之后,比如名字为db_link_b ...

  5. 更换ubuntu apt-get 源

    为了优化ubuntu软件安装/更新速度,我测试了国内几家apt源的速度,发现北京交大的apt源速度相对最快,然后可以通过以下步骤更新ubuntu源 1) 备份默认的apt源 $ cd /etc/apt ...

  6. Date.UTC日期格式

    日期格式 %a: 简短型星期,比如‘Mon’. %A: 完整型星期, 比如‘Monday’. %d: 两位的日期, 从01到31. %e: 数字型日期,从 1 到 31. %b: 简短型月份, 比如  ...

  7. [JS]以下是JS省市联动菜单代码

    以下是JS省市联动菜单代码: 代码一: <html> <head> <title></title> <script language=" ...

  8. nyoj 88 汉诺塔(一)

    点击打开链接 汉诺塔(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝 ...

  9. ibatis配置多表关联(一对一、一对多、多对多)

    iBatis的多表关联. ibatis的表关联,和数据库语句无关,是在程序中,把若干语句的结果关联到一起.这种关联形式,虽然在大数据量时是很奢侈的行为,但是看起来很干净,用起来也很方便. 这里用表lo ...

  10. 【转】SQL Server 2008下载 (附注册码)

    SQL Server 2008 中文试用版下载地址:http://sqlserver.dlservice.microsoft.com/dl/download/B/8/0/B808AF59-7619-4 ...