[转]WF事件驱动(4) -持久化

本文转自:http://www.cnblogs.com/Mayvar/archive/2011/09/03/wanghonghua201109030451.html

前方三篇,作者介绍到了怎么样在WF
4中设计简约的审查批准流程,没有啥越发新鲜的技术,只不过WF4对于事件机制有了一点都不小的改良吧。

这一篇要来谈谈越发尖锐一些的话题:如若大家的流水生产线需求长日子才能到位(那是很广泛的),那么怎么样在这几个流程空闲(例如等待经理审查批准)的时候,更好地管理它们啊?

大家都了解,暗中认可情形下,全体流程实例都是在内部存款和储蓄器中被创建的1个对象。那么那里涉及的管住,有几个范畴的意趣:

  1. 尽管有个别实例处于空闲状态,那么她们所占用的内部存款和储蓄器恐怕是浪费的。
  2. 由于大概因意外境况导致的宕机(例如停电,可能被某些恶作剧者按下了重启按钮),所以放在内部存储器中的实例是很不保障的

故而,为了完毕地点的三个目标,WF
提供了所谓的“持久化”的机能。正是永葆大家将工作流实例通过自然的措施保存起来,等急需的时候再取出来即可。

WF3就伊始协助那种特征,那时候称之为“持久化服务”。WF4对此做了更为的精益求精和周详。本文首要就是商量WF4下边咋办持久化。

 

总体代码,请通过 这里
下载

 

1. 备选持久化数据库

WF的持久化功效私下认可是用三个SQL
Server的数据库来保存数据的。当然,在此基础上大家得以扩充。但平日选拔暗中认可的那么些数据库是明智的抉择。

WF4提供了四个本子,可以让我们来生成那一个数据库。那三个剧本平时在底下的目录

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SQL\en

【注意】如若你不是x64的系统,则恐怕是C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en

图片 1

大家得以手工业先在SQL Server Management Studio中创立二个数据库,例如叫WF4

图片 2

然后,将数据库上下文切换成WF4,依次运转五个本子

SqlWorkflowInstanceStoreSchema.sql

SqlWorkflowInstanceStoreLogic.sql

 

本条数据库的布局要是有趣味,能够研商一下。那里就只是多开始展览了

图片 3

 

2. 修改宿主程序,添加持久化服务的职能

数据库准备好现在,大家要求对宿主稍做修改,就足以做到持久化功用的布署

首先,我们必要在宿主程序中添加七个程序集引用

图片 4

修改Main方法代码如下 (请留意巴黎绿字体)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Activities;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;

using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Activities.Persistence;
using System.ServiceModel.Activities.Description;
using System.Xml.Linq;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new WorkflowServiceHost(
                new DocumentReviewLib.DocumentReviewWorkflow(),
                new Uri("http://localhost:8080/DRS"));

            host.AddDefaultEndpoints();//这个方法是添加了一些标准的端点

            host.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });

            host.AddServiceEndpoint(
                "IMetadataExchange",
                MetadataExchangeBindings.CreateMexHttpBinding(),
                "mex");


            var store = new SqlWorkflowInstanceStore(
                "server=(local)\\sqlexpress;database=WF4;integrated security=true");

            host.DurableInstancingOptions.InstanceStore = store;

            host.Open();
            Console.WriteLine("Server is ready.");
            Console.Read();

        }
    }
}

 

这么就足以做到劳动的布署了。当然,我们那边仅仅是为着直观时期,用了代码的办法(而且用的是最简易的做法)。在生产环境下,大家只怕会倾向于用配备文件的措施,而不是代码。

事实上,笔者个人认为WCF,WF中一个一点都不小的亮点正是减掉了对代码的正视度,确实照旧做得不错的。

 

 

3. 调节和测试程序

按下F5展开调节和测试

图片 5

图片 6

咱俩回到数据库看一下情景,请小心看那么些InstancesTable

图片 7

也等于说,它曾经把那三个实例保存到了数据库中。

 

下一场,大家接下去对流程展开审查批准

图片 8

很肯定,236738261以此流程已经终止了,大家再来看一下数据库中的记录

图片 9

我们看看,以往数据库中的记录数也成为了3条。

 

 

4.什么样加载已经保存好的实例

既然有那般一个数据库保存好了小编们的实例,那么就能够放心大胆地将宿主程序关闭掉。

作者们来看,数据库中的记录如故是在的。请小心,作者因为做了任何一些测试,所以今后实例有八个

图片 10

 

看起来很不利,不是吗?

不过有二个问题随之而来,当大家再一次打开应用程序的时候,大家兴许希望宿主程序能自行地加载这个实例的音信,可能说大家依旧能够对那一个实例实行操作。那要哪些来完毕吗?

请大家安分守纪作者的步调来做演练

4.1 将宿主程序开起来

图片 11

4.2 将客户端开起来

图片 12

点击“创设流程”按钮,能够多点三回

图片 13

图片 14

4.3 将宿主程序关闭掉

这么做的指标,是仿照一下服务器突然停电了恐怕类似那样的场合。

请不要将客户端关闭

4.4 重新打开宿主程序

那样做就模仿服务重视启的情景。那么,难点不怕,此时客户端还能够连续处理那几个未到位的流水生产线吗?

图片 15

笔者们可以选用多少个编号之后,依旧和原先那么,点击“同意”或然“拒绝”按钮

图片 16

咱俩发现,这一个流程如故得以继续处理的。而且,大家并不必要在服务端做任何尤其的设计。

就此,大家得以如此总计一下:当二个流水生产线的恳求被发送到服务端,WorkflowServiceHost会收到,它先在内部存款和储蓄器中寻觅看是或不是有适度的实例,假使没有,则会尝试查看数据库中是否有适当的实例,若是有,则会加载它

 

那么,如若在内部存款和储蓄器和数据库都尚未实例的话,会怎么啊?(例如有些流程已经被处理完了,你照旧硬要一而再审查批准)。那种景观下,WorkflowServiceHost会将这么些请求列为所谓的一无所长的音信。

为了证实那点,大家对服务器代码稍作修改(请留意金红部分)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Activities;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;

using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Activities.Persistence;
using System.ServiceModel.Activities.Description;
using System.Xml.Linq;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new WorkflowServiceHost(
                new DocumentReviewLib.DocumentReviewWorkflow(),
                new Uri("http://localhost:8080/DRS"));

            host.AddDefaultEndpoints();//这个方法是添加了一些标准的端点

            host.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });

            host.AddServiceEndpoint(
                "IMetadataExchange",
                MetadataExchangeBindings.CreateMexHttpBinding(),
                "mex");


            var store = new SqlWorkflowInstanceStore("server=(local)\\sqlexpress;database=WF4;integrated security=true");
            host.DurableInstancingOptions.InstanceStore = store;

            host.UnknownMessageReceived += (o, e) =>
            {
                Console.WriteLine("\n"+e.Message+"\n");
            };
            host.Open();



            Console.WriteLine("Server is ready.");
            Console.Read();

        }


    }
}

调节的时候,能够对3个数码一而再点击三回“同意”,则第2次会被视为非法的呼吁。如下图所示

 

图片 17

 

5. 怎么在客户端获取待处理职务列表

在上叁个演练中,大家为了测试服务器端会自动物检疫索那多少个未形成的流水生产线实例,大家将宿主程序关闭后再打开了,可是本人也专门提示大家,不要将客户端程序关闭。

何以吗?因为只要你关闭了,那1个编号就从未有过了,而作者辈UpdateTicket操作是要基于TicketId进行操作的。

那正是说,就引申出来一个更大的题材,客户端不容许永远开着的,那么那几个未到位流程的TicketId要保留在哪儿?而客户端又何以能博获得这么些列表呢?

 

一些朋友恐怕会说,大家能够单独搞一个数据库吧,用3个表来保存这个音信好了。那当然是能够的,但并不见得是很好的多少个做法。

在WF4所提供的持久化成效中,考虑到了那种题材。它可以用三个异样的表保存大家流程运行期间的一部分多少。那里一时半刻称为“流程数据”吧

为了促成那样的效益,必要对持久化进行须求的恢弘,请大家鲁人持竿本身上面包车型客车步子来操作。

5.1 创造3个PersistenceParticipant

那是所谓的持久化加入者。它将在持久化的长河中起一定的功能。

为了方便复用,咱们单独成立了四个ClassLibrary,取名为Extensions

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities.Persistence;
using System.Xml.Linq;

namespace Extensions
{
    public class MyInstanceStoreParticpant : PersistenceParticipant
    {

        public int TicketId { get; set; }
        XNamespace xNS = XNamespace.Get("http://xizhang.com/DocumentReview");

        /// <summary>
        /// 这个方法会在工作流实例被持久化的时候自动调用
        /// 这些数据是会被保存到InstancePromotedPropertiesTable这个表的
        /// </summary>
        /// <param name="readWriteValues"></param>
        /// <param name="writeOnlyValues"></param>
        protected override void CollectValues(out IDictionary<XName, object> readWriteValues, out IDictionary<XName, object> writeOnlyValues)
        {
            readWriteValues = new Dictionary<XName, object>();
            readWriteValues.Add(xNS.GetName("TicketId"), this.TicketId);

            writeOnlyValues = null;
        }

    }
}

图片 18

此间涉及叁个奇特的表:InstancePromotedPropertiesTable(正是在持久化这几个数据库中,本例为WF4),大家假如有时光足以看一下构造。它有70个字段。

再者,在这些类型中,我们还添加二个自定义的Activity,来落实真正的多上大夫存

图片 19

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;

namespace Extensions
{

    public sealed class SetTicket : CodeActivity
    {
        public InArgument<int> TicketId { get; set; }
        protected override void Execute(CodeActivityContext context)
        {
            var extension = context.GetExtension<MyInstanceStoreParticpant>();
            extension.TicketId = TicketId.Get(context);
        }
    }
}

 

5.2 在宿主中运用该扩充,并且设定要封存的音信

图片 20

 

代码也要做相应的改动,请留心鲜青部分

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Activities;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;

using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Activities.Persistence;
using System.ServiceModel.Activities.Description;
using System.Xml.Linq;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new WorkflowServiceHost(
                new DocumentReviewLib.DocumentReviewWorkflow(),
                new Uri("http://localhost:8080/DRS"));

            host.AddDefaultEndpoints();//这个方法是添加了一些标准的端点

            host.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });

            var store = new SqlWorkflowInstanceStore("server=(local)\\sqlexpress;database=WF4;integrated security=true");

            host.UnknownMessageReceived += (o, e) =>
            {
                Console.WriteLine("\n"+e.Message+"\n");
            };


            host.Description.Behaviors.Add(
                new WorkflowIdleBehavior()
                {
                    TimeToPersist = TimeSpan.FromSeconds(0)
                });

            XNamespace xNS = XNamespace.Get("http://xizhang.com/DocumentReview");
            store.Promote("DocumentReview",
                new List<XName>() { xNS.GetName("TicketId") },
                null);


            host.WorkflowExtensions.Add(new Extensions.MyInstanceStoreParticpant());


            host.DurableInstancingOptions.InstanceStore = store;
            host.Open();



            Console.WriteLine("Server is ready.");
            Console.Read();

        }


    }
}

 

5.3 使用工作流设计,使用自定义的Activity

请保管在DocumentReviewLib中添加了如下四个引用

图片 21

将自定义的Activity拖放咋合适岗位,并且让它的品质TicketId绑定到变量

图片 22

5.4 调节和测试程序

启航服务器和客户端,点击数13次后,到SSMS中查看
[WF4].[System.Activities.DurableInstancing].[InstancePromotedPropertiesTable]其一表的数据

图片 23

那么,怎么查询这一个多少吧?

实际也简单,咱们一般推荐在数据库中做3个视图,如

USE WF4
GO

CREATE VIEW DocumentReviewTask
AS
SELECT [SurrogateInstanceId]
      ,[PromotionName]
      ,[Value1] AS TicketId
FROM [WF4].[System.Activities.DurableInstancing].[InstancePromotedPropertiesTable]

 

询问这几个视图的结果如下

图片 24

 

5.5 在宿主程序中经过3个独特的服务,提供这几个列表给客户端

因为涉嫌到多少访问,大家那边用八个LINQ to SQL Class来简化开发

图片 25

从数据库少将这几个视图托拽到设计器中

图片 26

将宿主代码修改如下,请留心葡萄紫部分

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Activities;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;

using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Activities.Persistence;
using System.ServiceModel.Activities.Description;
using System.Xml.Linq;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new WorkflowServiceHost(
                new DocumentReviewLib.DocumentReviewWorkflow(),
                new Uri("http://localhost:8080/DRS"));

            host.AddDefaultEndpoints();//这个方法是添加了一些标准的端点

            host.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });

            var store = new SqlWorkflowInstanceStore("server=(local)\\sqlexpress;database=WF4;integrated security=true");

            host.UnknownMessageReceived += (o, e) =>
            {
                Console.WriteLine("\n"+e.Message+"\n");
            };


            host.Description.Behaviors.Add(
                new WorkflowIdleBehavior()
                {
                    TimeToPersist = TimeSpan.FromSeconds(0)
                });

            XNamespace xNS = XNamespace.Get("http://xizhang.com/DocumentReview");
            store.Promote("DocumentReview",
                new List<XName>() { xNS.GetName("TicketId") },
                null);


            host.WorkflowExtensions.Add(new Extensions.MyInstanceStoreParticpant());


            host.DurableInstancingOptions.InstanceStore = store;
            host.Open();


            var common = new ServiceHost(
                typeof(CommonService),
                new Uri("http://localhost:8080/Common"));

            common.AddServiceEndpoint(
                typeof(ICommonService).FullName,
                new BasicHttpBinding(),
                "");

            common.Open();

 Console.WriteLine("Server is ready."); Console.Read(); } }  [ServiceContract] public interface ICommonService { [OperationContract] int[] GetTicketIds(); } public class CommonService : ICommonService { public int[] GetTicketIds() { var ctx = new InstanceStoreDataContext(); return ctx.DocumentReviewTasks.Select(r => (int)r.TicketId).ToArray(); } } }

 

5.6 修改客户端,使用该服务

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel.Activities;
using System.ServiceModel;


namespace Client
{
    [ServiceContract]
    public interface ICommonService
    {
        [OperationContract]
        int[] GetTicketIds();
    }


    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            Load += new EventHandler(Form1_Load);
        }




        void Form1_Load(object sender, EventArgs e)
        {
            LoadTaskList();

        }

        private void LoadTaskList()
        {
            //加载所有没有完成的流程
            var factory = new ChannelFactory<ICommonService>(
                new BasicHttpBinding(), new EndpointAddress("http://localhost:8080/Common"));


            var proxy = factory.CreateChannel();

            var ids = proxy.GetTicketIds();
            foreach (var item in ids)
            {
                lstTickets.Items.Add(item);
            }
        }

        private void btCreate_Click(object sender, EventArgs e)
        {
            var proxy = new DocumentReviewClient();
            var result = proxy.CreateTicket();

            lstTickets.Items.Add(result);
        }

        private void btApproval_Click(object sender, EventArgs e)
        {
            //同意某个流程
            var action = "approval";
            UpdateTicket(action);

        }

        private void UpdateTicket(string action)
        {
            if (lstTickets.SelectedIndex > -1)
            {
                var id = int.Parse(lstTickets.SelectedItem.ToString());
                var comment = txtComment.Text;
                var proxy = new DocumentReviewClient();
                proxy.UpdateTicket(action, comment, id);

            }
        }

        private void btReject_Click(object sender, EventArgs e)
        {
            var action = "Reject";
            UpdateTicket(action);
        }

    }
}

5.7 调节和测试程序

图片 27

 

 

计算:我用了四篇小说介绍了依照WF4完毕审批流程的二个例证,通超过实际例能够支持大家更好地了解有关的技术。

一体化代码,请通过 这里
下载

相关文章