WF----Host工作流的改进方法

本文介绍如何使用自定义封装类和工作流管理类来管理工作流实例,包括创建、启动、等待完成及处理日志信息,以及如何通过配置文件App.Config来简化服务配置过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 之前在网上看到一篇介绍WF的文章,写得真不错,所以特意转过来。需要能给学习WF的人提供一个参照。下面是介绍的内容:

在这个例子中,我们的目标就是解决前面提到的种种问题和限制。我们实现一个自定义的工作流管理类以及一个相关的工作流实例包装类。这些类将封装很多代码,用来host或者是与工作流运行时进行交互。实现管理类之后,我们开发一个宿主控制台程序,来执行上一个例子中的测试工作流。

 

实现工作流实例包装

工作流管理类应该包装起来,以便于其它的工程复用。我们创建一个Empty Workflow Project工程,命名为Nocturne.Workflow.Hosting。这是强命名,我们将在以后多次用到。一个常被复用的模块应该以这种命名方式存在于我们的机器里。

 

添加一个普通的C#类,名为WorkflowInstanceWrapper,然后添加下面的代码。说明已经包含在注释当中。

 

using System;

using System.Collections.Generic;

using System.Threading;

using System.Workflow.Runtime;

 

namespace Nocturne.Workflow.Hosting

{

     /// <summary>

     /// 工作流实例的容器

     /// </summary>

    [Serializable]

    public class WorkflowInstanceWrapper

     {

        private WorkflowInstance _workflowInstance;

        private ManualResetEvent _waitHandle = new ManualResetEvent(false);

 

        private Dictionary<string, object> _outputParameters = new Dictionary<string, object>();

        private Exception _exception;

        private string _reasonSuspended = string.Empty;

 

        public WorkflowInstanceWrapper(WorkflowInstance instance)

        {

            _workflowInstance = instance;

        }

 

        #region 公开属性与方法

 

        /// <summary>

        /// 获取工作流实例的 ID

        /// </summary>

        public Guid Id

        {

            get

            {

                if (_workflowInstance != null)

                {

                    return _workflowInstance.InstanceId;

                }

                else

                {

                    return Guid.Empty;

                }

            }

        }

 

        /// <summary>

        /// 输出参数的集合

        /// </summary>

        public Dictionary<string, object> OutputParameters

        {

            get { return _outputParameters; }

            set { _outputParameters = value; }

        }

 

        /// <summary>

        /// 一个等待句柄。宿主应用程序可以使用它,挂起

        /// 进程直到工作流完成。

        /// </summary>

        public ManualResetEvent WaitHandle

        {

            get { return _waitHandle; }

            set { _waitHandle = value; }

        }

 

        /// <summary>

        /// 工作流可能抛出的异常对象

        /// </summary>

        public Exception Exception

        {

            get { return _exception; }

            set { _exception = value; }

        }

 

        /// <summary>

        /// 工作流挂起的原因

        /// </summary>

        public string ReasonSuspended

        {

            get { return _reasonSuspended; }

            set { _reasonSuspended = value; }

        }

 

        /// <summary>

        /// 真实的工作流实例

        /// </summary>

        public WorkflowInstance WorkflowInstance

        {

            get { return _workflowInstance; }

        }

 

        /// <summary>

        /// 工作流完成的信号,宿主应用程序可以停止等待

        /// </summary>

        public void StopWaiting()

        {

            _waitHandle.Set();

        }

 

        #endregion

    }

}

 

实现工作流管理类

接下来我们来实现工作流管理类。它的目的就在于处理与WorkflowRuntime实例的主要交互行为,并不是代替WorkflowRuntime类,常规的任务比如创建和启动工作流都由此类来处理。从这一点上来说,很像是三层程序结构模型里的“数据访问层”。

 

该类同样包含了WorkflowRuntime的事件处理句柄。当一个工作流完成或中断的时候,这个处理代码会在集合中找到WorkflowInstanceWrapper,设置输出参数、异常,或是挂起原因。这就可以同时保存工作流的各种结果,交由宿主程序处理。

 

添加一个WorkflowRuntimeManager类,代码如下:

 

using System;

using System.Collections.Generic;

using System.Threading;

using System.Workflow.Runtime;

 

namespace Nocturne.Workflow.Hosting

{

    public class WorkflowRuntimeManager : IDisposable

    {

        // 定义两个实例变量,_workflowRuntime变量保存着到WorkflowRuntime实例的引用

        // _workflows变量是一个以Guid为键值的WorkflowInstanceWrapper的字典。

 

        private WorkflowRuntime _workflowRuntime;

        private Dictionary<Guid, WorkflowInstanceWrapper> _workflows = new Dictionary<Guid, WorkflowInstanceWrapper>();

 

        /// <summary>

        /// 构造函数。

        /// </summary>

        /// <param name="instance"></param>

        public WorkflowRuntimeManager(WorkflowRuntime instance)

        {

            _workflowRuntime = instance;

            if (instance == null)

            {

                throw new NullReferenceException("需要一个非空的 WorkflowRuntime 实例");

            }

 

            // 提交所有的工作流事件

            SubscribeToEvents(instance);

        }

 

        /// <summary>

        /// 创建并启动一个工作流。

        /// </summary>

        /// <param name="workflowType">待启动的工作流类型</param>

        /// <param name="parameters">入口参数</param>

        /// <returns>包装在一个WorkflowInstanceWrapper里的工作流实例</returns>

        public WorkflowInstanceWrapper StartWorkflow(Type workflowType, Dictionary<string, object> parameters)

        {

            WorkflowInstance instance = _workflowRuntime.CreateWorkflow(workflowType, parameters);

            WorkflowInstanceWrapper wrapper = AddWorkflowInstance(instance);

            instance.Start();

            return wrapper;

        }

 

        #region 公共属性和事件

 

        /// <summary>

        /// 获取WorkflowRuntime实例

        /// </summary>

        public WorkflowRuntime WorkflowRuntime

        {

            get { return _workflowRuntime; }

        }

 

        /// <summary>

        /// 获取工作流实例包装的一个字典

        /// </summary>

        public Dictionary<Guid, WorkflowInstanceWrapper> Workflows

        {

            get { return _workflows; }

        }

 

        /// <summary>

        /// 记录该类的日志信息的事件

        /// </summary>

        public event EventHandler<WorkflowLogEventArgs> MessageEvent;

 

        #endregion

 

        #region 工作流集合管理

 

        /// <summary>

        /// 从工作流字典中移除某个实例

        /// </summary>

        /// <param name="workflowId"></param>

        public void ClearWorkflow(Guid workflowId)

        {

            if (_workflows.ContainsKey(workflowId))

            {

                _workflows.Remove(workflowId);

            }

        }

 

        /// <summary>

        /// 从字典中清除所有的工作流

        /// </summary>

        public void ClearAllWorkflows()

        {

            _workflows.Clear();

        }

 

        /// <summary>

        /// 向字典中添加一个新的工作流实例

        /// </summary>

        /// <param name="instance"></param>

        /// <returns></returns>

        private WorkflowInstanceWrapper AddWorkflowInstance(WorkflowInstance instance)

        {

            WorkflowInstanceWrapper wrapper = null;

            if (!_workflows.ContainsKey(instance.InstanceId))

            {

                wrapper = new WorkflowInstanceWrapper(instance);

                _workflows.Add(wrapper.Id, wrapper);

            }

 

            return wrapper;

        }

 

        /// <summary>

        /// 依据Guid查找工作流实例

        /// </summary>

        /// <param name="workflowId"></param>

        /// <returns></returns>

        public WorkflowInstanceWrapper FindWorkflowInstance(Guid workflowId)

        {

            WorkflowInstanceWrapper result = null;

            if (_workflows.ContainsKey(workflowId))

            {

                result = _workflows[workflowId];

            }

            return result;

        }

 

        /// <summary>

        /// 等待所有的工作流实例完成

        /// </summary>

        /// <param name="msecondsTimeout"></param>

        public void WaitAll(int msecondsTimeout)

        {

            if (_workflows.Count > 0)

            {

                WaitHandle[] handles = new WaitHandle[_workflows.Count];

                int index = 0;

                foreach (WorkflowInstanceWrapper wrapper in _workflows.Values)

                {

                    handles[index] = wrapper.WaitHandle;

                    index++;

                }

                WaitHandle.WaitAll(handles, msecondsTimeout, false);

            }

        }

 

        #endregion

 

        #region IDisposable Members

 

        /// <summary>

        /// 销毁工作流运行时

        /// </summary>

        public void Dispose()

        {

            if (_workflowRuntime != null)

            {

                _workflowRuntime.StopRuntime();

                _workflowRuntime.Dispose();

            }

            ClearAllWorkflows();

        }

 

        #endregion

 

        #region 工作流事件处理

 

        /// <summary>

        /// 订阅我们所有关心的事件

        /// </summary>

        /// <param name="runtime"></param>

        private void SubscribeToEvents(WorkflowRuntime runtime)

        {

 

            runtime.Started += new EventHandler<WorkflowRuntimeEventArgs>(runtime_Started);

            runtime.Stopped += new EventHandler<WorkflowRuntimeEventArgs>(runtime_Stopped);

            runtime.WorkflowAborted += new EventHandler<WorkflowEventArgs>(runtime_WorkflowAborted);

            runtime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(runtime_WorkflowCompleted);

            runtime.WorkflowCreated += new EventHandler<WorkflowEventArgs>(runtime_WorkflowCreated);

            runtime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(runtime_WorkflowIdled);

            runtime.WorkflowLoaded += new EventHandler<WorkflowEventArgs>(runtime_WorkflowLoaded);

            runtime.WorkflowPersisted += new EventHandler<WorkflowEventArgs>(runtime_WorkflowPersisted);

            runtime.WorkflowResumed += new EventHandler<WorkflowEventArgs>(runtime_WorkflowResumed);

            runtime.WorkflowStarted += new EventHandler<WorkflowEventArgs>(runtime_WorkflowStarted);

            runtime.WorkflowSuspended += new EventHandler<WorkflowSuspendedEventArgs>(runtime_WorkflowSuspended);

            runtime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(runtime_WorkflowTerminated);

            runtime.WorkflowUnloaded += new EventHandler<WorkflowEventArgs>(runtime_WorkflowUnloaded);

        }

 

        void runtime_Started(object sender, WorkflowRuntimeEventArgs e)

        {

            LogStatus(Guid.Empty, "Started");

        }

 

        void runtime_Stopped(object sender, WorkflowRuntimeEventArgs e)

        {

            LogStatus(Guid.Empty, "Stopped");

        }

 

        void runtime_WorkflowCreated(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowCreated");

        }

 

        void runtime_WorkflowStarted(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowStarted");

        }

 

        void runtime_WorkflowIdled(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowIdled");

        }

 

        void runtime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowCompleted");

            WorkflowInstanceWrapper wrapper = FindWorkflowInstance(e.WorkflowInstance.InstanceId);

            if (wrapper != null)

            {

                wrapper.OutputParameters = e.OutputParameters;

                wrapper.StopWaiting();

            }

        }

 

        void runtime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowTerminated");

            WorkflowInstanceWrapper wrapper = FindWorkflowInstance(e.WorkflowInstance.InstanceId);

            if (wrapper != null)

            {

                wrapper.Exception = e.Exception;

                wrapper.StopWaiting();

            }

        }

 

        void runtime_WorkflowSuspended(object sender, WorkflowSuspendedEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowSuspended");

            WorkflowInstanceWrapper wrapper = FindWorkflowInstance(e.WorkflowInstance.InstanceId);

            if (wrapper != null)

            {

                wrapper.ReasonSuspended = e.Error;

            }

        }

 

        void runtime_WorkflowResumed(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowResumed");

        }

 

        void runtime_WorkflowPersisted(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowPersisted");

        }

 

        void runtime_WorkflowLoaded(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowLoaded");

        }

 

        void runtime_WorkflowAborted(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowAborted");

            WorkflowInstanceWrapper wrapper = FindWorkflowInstance(e.WorkflowInstance.InstanceId);

            if (wrapper != null)

            {

                wrapper.StopWaiting();

            }

        }

 

        void runtime_WorkflowUnloaded(object sender, WorkflowEventArgs e)

        {

            LogStatus(e.WorkflowInstance.InstanceId, "WorkflowUnloaded");

        }

 

        private void LogStatus(Guid instanceId, String msg)

        {

            if (MessageEvent != null)

            {

                String formattedMsg;

                if (instanceId == Guid.Empty)

                {

                    formattedMsg = String.Format("Runtime - {0}", msg);

                }

                else

                {

                    formattedMsg = String.Format("{0} - {1}", instanceId, msg);

                }

 

                // 引发事件

                MessageEvent(this, new WorkflowLogEventArgs(formattedMsg));

            }

        }

 

        #endregion

    }

 

    /// <summary>

    /// 在管理类中用来记录日志信息的EventArgs

    /// </summary>

    public class WorkflowLogEventArgs : EventArgs

    {

        private string _msg = string.Empty;

        public WorkflowLogEventArgs(string msg)

        {

            _msg = msg;

        }

 

        public string Message

        {

            get { return _msg; }

        }

    }

}

 

这其中运用到了事件、委托等.NET概念,如果有不是很清楚的地方,可以暂时略过。但作为进阶的必备知识,还是借机多多复习为好~~~

 

使用Workflow Manager

为了展示如何去使用这个管理类,我们可以实现另外一个控制台应用程序,来执行我们前面提到的那个例子。创建一个新的工程,选择Sequantial Workflow Console Application模板,命名为ConsoleHostingManaged。我们还需要向此工程添加两个引用,一是SharedWorkflows,它包含了测试用的工作流;另一个就是Nocturne.Workflow.Hosting工程,它包含了我们刚刚开发的工作流管理类。创建完毕之后,删掉Workflow1.cs

 

在演示使用管理类来host工作流之外,这个应用程序还展示了如何去装载一个核心工作流服务。我们执行测试的代码将被放置在一个单独的类里,而Program.cs将仅仅包含少量的初始化代码。

 

添加一个新的普通类WorkflowTest,其完整内容如下:

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Hosting;

 

using Nocturne.Workflow.Hosting;

 

namespace ConsoleHostingManaged

{

    /// <summary>

    /// 使用自定义封装类来host工作流

    /// </summary>

     class WorkflowTest

     {

        /// Run 方法将在 Main 方法启动之后被执行,该方法创建一个 WorkflowRuntimeManager 的实例而启动,

        /// 使用 using 的目的在于一旦离开这个语句块,就可以自动调用 Dispose 方法来销毁对象,前面实现

        /// IDisposable 接口的意义就在于此。

        ///   

        /// WorkflowRuntimeManager 构造函数被传入一个 WorkflowRuntime 类的实例。这就是我们设计管理类

        /// 的目的,从外部构造 WorkflowRuntime,以此来提供附加的过占星。

        ///    

        /// 一个事件句柄被作为 WorkflowRuntimeManager.MessageEvent 添加。此事件在处理 WorkflowRuntime

        /// 事件的时候会被出发。这段代码仅仅是简单的将信息输出到屏幕上

       

        /// <summary>

        /// 启动工作流

        /// </summary>

        public static void Run()

        {

            Console.WriteLine("使用 persistence 服务运行测试");

 

            using (WorkflowRuntimeManager manager = new WorkflowRuntimeManager(new WorkflowRuntime()))

            {

                // 添加一个事件句柄记录日志

                manager.MessageEvent += delegate(object sender, WorkflowLogEventArgs e)

                {

                    Console.WriteLine(e.Message);

                };

 

                // 创建完 WorkflowRuntime 及其包装之后,我们采用私有的 AddServices 方法向工作流运行

                // 时添加服务。

                AddServices(manager.WorkflowRuntime);

 

                // 启动工作流运行时。注意这一步并非必须,如果忽略这条语句,工作流运行时同样会自动运

                // 行。但是所有的核心服务必须在启动运行时引擎之前声明,如果我们在调用这条语句之后

                // (或者被自动调用了)才添加核心服务(也就是上面那句话),编译器会抛出异常。

                manager.WorkflowRuntime.StartRuntime();

 

                // 当核心服务添加到运行时引擎之后,就可以运行工作流了。Dictionary 就是用来传送参数的

                // 桥梁

                Dictionary<string, object> wfArguments = new Dictionary<string, object>();

                wfArguments.Add("InputString", "One");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1), wfArguments);

               

                // 启动另一个实例。

                wfArguments.Clear();

                wfArguments.Add("InputString", "two");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1),wfArguments);

 

                wfArguments.Clear();

                wfArguments.Add("InputString","three");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1),wfArguments);

 

                // 等待所有工作流实例完成

                manager.WaitAll(15000);

 

                // 显示所有工作流实例的结果。由于这些工作流是存储在Dictionary里的,因此可以

                // 使用 foreach 进行迭代

                foreach(WorkflowInstanceWrapper wrapper in manager.Workflows.Values)

                {

                    if (wrapper.OutputParameters.ContainsKey("Result"))

                    {

                        Console.WriteLine(wrapper.OutputParameters["Result"]);

                    }

                    else

                    {

                        if (wrapper.Exception!=null)

                        {

                            Console.WriteLine("{0} - 异常: {1}",wrapper.Id,wrapper.Exception.Message);

                        }

                    }

                }

 

                manager.ClearAllWorkflows();

            }

        }

       

        /// <summary>

        /// 添加运行时引擎需要的服务

        /// </summary>

        /// <param name="instance"></param>

        private static void AddServices(WorkflowRuntime instance)

        {

            string connStringPersistence = string.Format("Initial Catalog={0}; Data Source={1}; Integrated Security={2};", "WorkflowPersistence", @"localhost\SQLEXPRESS", "SSPI");

 

            instance.AddService(new SqlWorkflowPersistenceService(connStringPersistence, true, new TimeSpan(0, 2, 0), new TimeSpan(0, 0, 5)));

        }

     }

}

 

在最后的部分可以看到,我们用到的是SqlWorkflowPersistenceService,这就需要我们事先安装过SQL Server。在这里我们以SQL Server 2005 Express为例,在数据库中建立WorkflowPersistence所需的基本表结构。

l  建立数据库。

进入命令行,执行

sqlcmd –S localhost\SQLEXPRESS –E –Q “create database WorkflowPersistence”

当然,如果你的SQL Server不是默认配置,请酌情修改加粗的数据库名称

l  建立数据表以及存储过程。

在命令行下切换路径到%systemroot%\Microsoft.Net\Framework\v3.0\Windows Workflow Foundation\SQL\EN,执行(如果是中文版的,则粗体部分应是CN

sqlcmd -S localhost\SQLEXPRESS -E -d WorkflowPersistence -i SqlPersistenceService_Schema.sql

sqlcmd -S localhost\SQLEXPRESS -E -d WorkflowPersistence -i SqlPersistenceService_Logic.sql

 

尽量不要修改数据库的名字,否则还需要修改WorkflowTest.csApp.Config文件。

 

SqlWorkflowPersistenceService接受下面的参数:

ConnectionString:这是SQL服务器的连接字符串,在这里就不多做介绍了;

UnloadOnIdle:一个布尔值,决定工作流在空闲的时候是否从数据库中卸装;

InstanceOwnershipDuration:一个TimeSpan类型,只是工作流运行时的实例持续其所有者身份多长时间。主要用在有很多的工作流工作在相同的persistence存储空间时;

LoadingInterval:也是一个TimeSpan,决定了persistence服务在工作流重新装载之前有多久的空闲时间。

 

运行测试

剩下来我们需要做的,就是向Main方法添加代码,来调用WorkflowTest类的Run方法。代码如下:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Hosting;

 

namespace ConsoleHostingManaged

{

    /// <summary>

    /// 使用自定义封装类测试工作流

    /// </summary>

    public class Program

    {

        static void Main(string[] args)

        {

            WorkflowTest.Run();

 

            Console.WriteLine("Press Enter to exit");

            Console.ReadLine();

        }

    }

}

 

运行之,看看它的结果。

 



 

在这个结果中,我们可以看到Runtime Started事件最先接收到,对应着StartRuntime方法。在这之后,创建了三个工作流。如果我们顺藤摸瓜,可以发现事件是按照下面的顺序触发的。

1.         Created:工作流实例被创建;

2.         Started:工作流实例开始执行;

3.         Idled:工作流成为非活动进程。注意到我故意使用DelayActivity产生了延时,此时工作流就处于空闲状态;

4.         Persisted:因为装载了persistence服务,工作流在空闲状态时会被persist到数据库中;

5.         Unloaded:即UnloadOnIdle时限触发工作流从内存中卸载;

6.         Loaded:在DelayActivity完成之后,工作流将被persistence服务装载回内存中;

7.         Persisted:该时间标志着工作流从数据库中被移除;

8.         Completed:工作流最终完成。

 

使用App.Config配置运行时

前面的示例演示了如何在代码中向工作流运行时添加核心服务。如果我们愿意,也可以采用应用程序配置文件(App.Config)来完成。这样做的好处就在于如果我们需要修改服务配置的时候,无需重新编译。

 

.NET应用程序配置文件是一个保存有多项配置的XML文件,具体的使用就不在这里详述。记着一点,配置文件的文件名是严格要求的,比如一个应用程序MyApp.exe,它的配置文件就必须为MyApp.exe.configASP.NET程序则统一使用Web.Config文件。

 

OK,现在开始。向ConsoleHostingManaged工程添加一个新的Item,将其类型选择为Application Configuration File,并且使用缺省的命名。完整内容如下:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="WorkflowRuntime"

             type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection,

             System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral,

             PublicKeyToken=31bf3856ad364e35" />

  </configSections>

  <WorkflowRuntime Name="ConsoleHostingManaged">

    <CommonParameters>

      <!--向所有服务添加公共属性-->

      <add name="ConnectionString"

           value="Initial Catalog=WorkflowPersistence;

           Data Source=localhost\SQLEXPRESS;

           Integrated Security=SSPI;" />

    </CommonParameters>

    <Services>

      <!--添加核心服务-->

      <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService,

           System.Workflow.Runtime, Version=3.0.00000.0,

           Culture=neutral, PublicKeyToken=31bf3856ad364e35"

           UnloadOnIdle="true" LoadIntervalSeconds="5" />

    </Services>

  </WorkflowRuntime>

</configuration>

 

仅仅有了这个文件还不行,代码也需要进行一些修改。首先向工程添加一个引用,System.Configuration程序集。然后做一份WorkflowTest.cs的副本,重命名为WorkflowAppConfigTest.cs,记得将类名也修改一下。修改后的代码如下所示:

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Hosting;

 

using Nocturne.Workflow.Hosting;

 

/// 必须添加 System.Configuration 程序集的引用,这样程序才能从 App.Config 中

/// 读取配置信息

 

namespace ConsoleHostingManaged

{

    /// <summary>

    /// 使用自定义封装类来host工作流,以及 App.Config 作为配置来源

    /// </summary>

     class WorkflowAppConfigTest

     {

        public static void Run()

        {

            Console.WriteLine("使用 App.Config 运行测试");

 

            using (WorkflowRuntimeManager manager = new WorkflowRuntimeManager(new WorkflowRuntime("WorkflowRuntime")))

            {

                // 添加一个事件句柄记录日志

                manager.MessageEvent += delegate(object sender, WorkflowLogEventArgs e)

                {

                    Console.WriteLine(e.Message);

                };

 

                // 启动工作流运行时。

                manager.WorkflowRuntime.StartRuntime();

 

                // Dictionary 就是用来传送参数的桥梁

                Dictionary<string, object> wfArguments = new Dictionary<string, object>();

                wfArguments.Add("InputString", "One");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1), wfArguments);

               

                // 启动另一个实例。

                wfArguments.Clear();

                wfArguments.Add("InputString", "two");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1),wfArguments);

 

                wfArguments.Clear();

                wfArguments.Add("InputString","three");

                manager.StartWorkflow(typeof(SharedWorkflows.Workflow1),wfArguments);

 

                // 等待所有工作流实例完成

                manager.WaitAll(15000);

 

                // 显示所有工作流实例的结果。

                foreach(WorkflowInstanceWrapper wrapper in manager.Workflows.Values)

                {

                    if (wrapper.OutputParameters.ContainsKey("Result"))

                    {

                        Console.WriteLine(wrapper.OutputParameters["Result"]);

                    }

                    else

                    {

                        if (wrapper.Exception!=null)

                        {

                            Console.WriteLine("{0} - 异常: {1}",wrapper.Id,wrapper.Exception.Message);

                        }

                    }

                }

 

                manager.ClearAllWorkflows();

            }

        }

     }

}

 

最重要的改动就是代码中粗体的部分。这一次我们使用的是WorkflowRuntime类的一个重载版本的构造函数,可以接受配置区域的名字。WorkflowRuntime字符串对应着我们在App.Config中添加的那个部分。AddServices方法就被移除了,因为它已经没有利用价值了……

 

修改Program.cs,将WorkflowTest.Run修改为WorkflowAppConfigText.Run即可。下面就是运行结果,除了Guid值有变化,其余部分都是相同的。Persistence服务的全部动作都可以从这份记录中看出来。

 



源文章地址:http://ustc.blog.hexun.com/15057034_d.html

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值