创建和运行长时间运行的工作流(二)

第三步:运行工作流

回顾上一部分中创建的工作流实例,看看其中我们创建的变量和参数。

创建和运行长时间运行的工作流(二)_第1张图片

我们在StateMachineNumberGuessWorkflow定义了两个变量和两个参数,分别如下:

变量Guess和Target:存储用户输入的猜测值和目标值,在后面的State中会多次用到。

输入参数MaxNumber:接受输入的猜测值上限

输出参数Turns:返回猜测的次数

WorkflowApplication.ResumeBookmark方法被调用后,NativeActivity将被恢复,我们在ReadInt.Execute()中指定的回调函数OnReadComplete BookmarkCallback将会被调用。 方法OnReadComplete的state参数就是ResumeBookmark方法的第二个参数,也就是用户输入的猜测值。

 

运行工作流的代码如下:

using System;
using System.Activities;
using System.Collections.Generic;
using NumberGuessWorkflowActivities;

namespace DirectWorkflowHost
{
    class Program
    {
        private static WorkflowApplication wfApp = null;
        static bool isCorrect = false;


        static void Main(string[] args)
        {
            NewGame();
            EnterGuess();

            Console.Read();
        }


        //循环猜值
        private static void EnterGuess()
        {
            while (isCorrect == false)
            {
                int guess = Convert.ToInt32(Console.ReadLine());

                //恢复具有名称为EnterGuess的书签
                wfApp.ResumeBookmark("EnterGuess", guess);
            }
        }


        //建立新游戏
        private static void NewGame()
        {
            var inputs = new Dictionary<string, object>();
            inputs.Add("MaxNumber", 20);//"MaxNumber"和StateMachineNumberGuessWorkflow的名为MaxNumber的输入参数对应

            WorkflowIdentity identity = new WorkflowIdentity()
            {
                Name = "StateMachineNumberGuessWorkflow",
                //使用指定的主版本号、次版本号、内部版本号和修订号初始化 System.Version 类的新实例。
                Version = new Version(1, 2, 3, 4)
            };

            //创建活动
            Activity wf = new StateMachineNumberGuessWorkflow();

            //为工作流的单个实例提供宿主
            wfApp = new WorkflowApplication(wf, inputs, identity);
            ConfigureWorkflowApplication(wfApp);
            wfApp.Run();
        }


        private static void ConfigureWorkflowApplication(WorkflowApplication wfApp)
        {
            //获取或设置工作流实例完成时调用的 System.Action<T>。
            wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
            {
                //活动处于出错状态。
                if (e.CompletionState == ActivityInstanceState.Faulted)
                {
                    Console.WriteLine(string.Format("Workflow Terminated. Exception:{0}\r\n{1}",
                        e.TerminationException.GetType().FullName,
                        e.TerminationException.Message));
                }
                //活动处于已取消状态
                else if (e.CompletionState == ActivityInstanceState.Canceled)
                {
                    Console.WriteLine("Workflow Canceled.");
                }
                else
                {
                    //获取工作流实例的输出参数
                    int turns = Convert.ToInt32(e.Outputs["Turns"]);
                    Console.WriteLine(string.Format("Congratulations, you guessed the number in {0} turns.", turns));

                    //当猜值成功后,工作流停止,结束程序
                    isCorrect = true;
                }
            };
        }
    }
}

运行结果如下:

创建和运行长时间运行的工作流(二)_第2张图片

 

第四部、持久化工作流实例

1、使用持久化方式保存工作流实例时首先创建一个数据库WF45GettingStartedTutorial。

2、创建相关表。创建表的脚步存放在一下路径:C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en,文件一共有两个,分别是:SqlWorkflowInstanceStoreSchema.sql和SqlWorkflowInstanceStoreSchema.sql,依次运行这两个脚本,会创建架构和表,并向表中插入一些基础数据。

3、通过持久化,可以将工作流实例WorkflowApplicationInstance的信息保存到数据库,所以我们可以先创建一个工作流宿主WorkflowApplication wfApp1,调用wfApp.Run()方法运行工作流,在wfApp.PersistableIdle事件触发时,自动调用关联的InstanceStore对象保存WorkflowApplicationInstance信息。

保存成功后,我们可以在任何时间创建新的工作流宿主WorkflowApplication wfApp2,重新从存储区中提取WorkflowApplicationInstance信息 instance,然后让wfApp2加载instancewfApp2.Load(instance); 这样就可以获取原来执行了部分流程的工作流实例,继续进行我们的操作。

工作流实例是同一个实例,但是宿主可以不同。为了方便,现在使用一个Winform窗体项目PersistWorkflowHost进行演示。

代码如下:

using System;
using System.Activities;
using System.Activities.DurableInstancing;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Windows.Forms;
using NumberGuessWorkflowActivities;

namespace PersistWorkflowHost
{
    public partial class UnLoadWorkflowHostForm : Form
    {
        const string connectionString = "Server=.;Initial Catalog=WF45GettingStartedTutorial;integrated security=true";
        SqlWorkflowInstanceStore store;
        bool workflowStarting;

        /// <summary> 当前工作流实例Id </summary>
        public Guid WorkflowInstanceId
        {
            get
            {
                return this.cboInstanceId.SelectedIndex == -1 ? Guid.Empty : (Guid)cboInstanceId.SelectedItem;
            }
        }

        public UnLoadWorkflowHostForm()
        {
            InitializeComponent();
        }

        private void WorkflowHostForm_Load(object sender, EventArgs e)
        {
            store = new SqlWorkflowInstanceStore(connectionString);

            //CreateDefaultInstanceOwner使用指定实例存储、定义标识和标识筛选器和超时间隔,创建工作流的默认实例所有者。
            WorkflowApplication.CreateDefaultInstanceOwner(store, null, WorkflowIdentityFilter.Any);

            cboRange.SelectedIndex = 0;

            if (cboInstanceId.Items.Count > 0)
                cboInstanceId.SelectedIndex = 0;

            ListPersistenceWorkflows();
        }

        /// <summary>列出所有已经保存的工作流实例Id</summary>
        private void ListPersistenceWorkflows()
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                //持久化后的工作流数据保存在Instances表中
                string cmdText = "Select [InstanceId] from [System.Activities.DurableInstancing].[Instances] Order By [CreationTime]";

                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = cmdText;
                conn.Open();

                using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (reader.Read())
                    {
                        Guid id = Guid.Parse(reader[0].ToString());
                        cboInstanceId.Items.Add(id);
                    }
                }
            }
        }

        private void cboInstanceId_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cboInstanceId.SelectedItem == null)
                return;

            txtStatus.Clear();

            if (!workflowStarting)
            {
                //WorkflowApplicationInstance指定有关工作流应用程序实例的信息。
                WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(this.WorkflowInstanceId, store);
                lblVersion.Text = instance.InstanceId.ToString();

                //使状态机转换到放弃状态。由于调用GetInstance方法后,该工作流应用程序实例将被工作流宿主锁定。
                //所以需要调用Abandon方法做一个类似解锁的操作。否则在后续的EnterGuess_Click方法中就无法从存储区中获取工作流实例信息了。
                instance.Abandon();
            }
        }

        private void NewGame_Click(object sender, EventArgs e)
        {
            //创建工作流运行时所需的输入参数
            var inputs = new Dictionary<string, object>();
            inputs.Add("MaxNumber", Convert.ToInt32(cboRange.SelectedItem));

            WorkflowIdentity identity = new WorkflowIdentity()
            {
                Name = "StateMachineNumberGuessWorkflow",
                //主版本号,次要版本号,内建版本号和修订号
                Version = new Version(1, 2, 3, 4)
            };

            Activity wf = new StateMachineNumberGuessWorkflow();
            WorkflowApplication wfApp = new WorkflowApplication(wf, inputs, identity);

            //由于工作流实例还未保存到存储区(数据库),所以当cboInstanceId的SelectedIndexChanged事件被触发时,还
            //不能调用WorkflowApplication.GetInstance去从存储区读取实例信息。所以将workflowStarting设为true值。
            //工作流宿主会保存到存储区的LockOwnersTable表
            workflowStarting = true;
            cboInstanceId.SelectedIndex = cboInstanceId.Items.Add(wfApp.Id);
            lblVersion.Text = identity.ToString();
            workflowStarting = false;

            ConfigureWorkflowApplication(wfApp);

            wfApp.Run();
        }

        //此方法配置 WorkflowApplication,添加所需扩展,并添加工作流生命周期事件的处理程序。
        private void ConfigureWorkflowApplication(WorkflowApplication wfApp)
        {
            wfApp.InstanceStore = store;

            //接下来创建 StringWriter 实例,并将它添加到 WorkflowApplication 的 Extensions 集合中。 在将 StringWriter 添加到扩展中时,
            //它会捕获所有 WriteLine 活动输出 。在工作流进入空闲状态时,可从 StringWriter 中提取 WriteLine 输出,并显示在窗体上。
            StringWriter sw = new StringWriter();
            wfApp.Extensions.Add(sw);

            wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
            {
                if (e.CompletionState == ActivityInstanceState.Faulted)
                {
                    UpdateStatus(string.Format("Workflow Terminated. Exception:{0}\r\n{1}",
                        e.TerminationException.GetType().FullName,
                        e.TerminationException.Message));
                }
                else if (e.CompletionState == ActivityInstanceState.Canceled)
                {
                    UpdateStatus("Workflow Canceled.");
                }
                else 
                {
                    int turns = Convert.ToInt32(e.Outputs["Turns"]);
                    UpdateStatus(string.Format("Congratulations, you guessed the number in {0} turns.", turns));
                }

                GameOver();
            };

            //wfApp.Aborted 获取或设置中止工作流实例时调用的 System.Action<T>。
            wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
            {
                UpdateStatus(string.Format("Workflow Aborted. Exception:{0}\r\n{1}",
                    e.Reason.GetType().FullName, e.Reason.Message));
            };

            //wfApp.OnUnhandledException获取或设置当前工作流实例遇到未处理的异常时调用的 System.Func<T,TResult>。
            wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
            {
                UpdateStatus(string.Format("Unhandled Exception:{0}\r\n{1}",
                    e.UnhandledException.GetType().FullName, e.UnhandledException.Message));
                GameOver();

                return UnhandledExceptionAction.Terminate;
            };

            //wfApp.PersistableIdle 获取或设置当前工作流实例处于空闲状态并可被保留时调用的 System.Activities.ActivityFunc。
            wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
            {
                //Send the current WriteLine outputs to the status window.
                var writers = e.GetInstanceExtensions<StringWriter>();
                foreach (var writer in writers)
                {
                    UpdateStatus(writer.ToString());
                }

                // 摘要:enum PersistableIdleAction    指定在允许持久性的情况下,当工作流进入空闲状态时发生的操作。 
                // None = 0,   指定不执行任何操作。 
                // Unload = 1,    指定 System.Activities.WorkflowApplication 应保持并卸载工作流。 
                // Persist = 2    指定 System.Activities.WorkflowApplication 应保持工作流。
                return PersistableIdleAction.Unload;
            };
        }

        private delegate void UpdateStatusDelegate(string msg);
        /// <summary>使用当前正在运行的工作流的状态来更新窗体上的状态窗口 </summary> 
        public void UpdateStatus(string msg)
        {
            if (txtStatus.InvokeRequired)
            {
                BeginInvoke(new UpdateStatusDelegate(UpdateStatus), msg);
            }
            else
            {
                if (!msg.EndsWith("\r\n"))
                    msg += "\r\n";
                txtStatus.Text += msg;

                txtStatus.SelectionStart = txtStatus.Text.Length;
                txtStatus.ScrollToCaret();//将控件内容滚动到当前插入符号位置
            }
        }

        private delegate void GameOverDelegate();
        /// <summary>此方法通过从“工作流实例 ID”组合框中删除已完成的工作流实例 ID 来更新窗体 </summary>
        private void GameOver()
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new GameOverDelegate(GameOver));
            }
            else
            {
                cboInstanceId.Items.Remove(cboInstanceId.SelectedItem);
                cboInstanceId.SelectedItem = null;
            }
        }

        private void btnQuit_Click(object sender, EventArgs e)
        {
            //Application.EnableVisualStyles();
            //Application.Run(new UnLoadWorkflowHostForm());
            this.Close();
        }

        private void EnterGuess_Click(object sender, EventArgs e)
        {
            if (this.WorkflowInstanceId == Guid.Empty)
            {
                MessageBox.Show("Please select a workflow.");
                return;
            }

            int guess;
            if (!Int32.TryParse(txtGuess.Text, out guess))
            {
                MessageBox.Show("Please enter an integer.");
                txtGuess.SelectAll();
                txtGuess.Focus();
                return;
            }

            //从存储区提取出之前保存的指定Id的工作流实例
            WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(this.WorkflowInstanceId, store);

            Activity wf = new StateMachineNumberGuessWorkflow();
            //这里不用提供输入参数了
            WorkflowApplication wfApp = new WorkflowApplication(wf, instance.DefinitionIdentity);
            ConfigureWorkflowApplication(wfApp);
            wfApp.Load(instance);
            wfApp.ResumeBookmark("EnterGuess", guess);

            txtGuess.Clear();
            txtGuess.Focus();
        }
    }
}

 


创建和运行长时间运行的工作流(一)

创建和运行长时间运行的工作流(二)

创建和运行长时间运行的工作流(三) 

 源代码

你可能感兴趣的:(创建和运行长时间运行的工作流(二))