如何编写长时间运行的活动来调用WF 4.0中的Web服务

我创建了一个执行Web请求的活动,并将结果存储到数据库中。 我发现对于这些长时间运行的活动,我应该编写一些不同的代码,以便不会阻止工作流引擎线程。

public sealed class WebSaveActivity : NativeActivity { protected override void Execute(NativeActivityContext context) { GetAndSave(); // This takes 1 hour to accomplish. } } 

我应该如何重写此活动以满足长时间运行的活动的要求

您可以使用例如ThreadPool.QueueUserWorkItem()在现有流程中生成一个线程,以便在需要时继续运行工作流的其余部分。 但是,请务必先了解multithreading和线程同步的含义。 或者您可以查看Hangfire或类似组件,将整个作业卸载到不同的进程中。

编辑:

根据您的评论,您可以查看基于任务的异步模式(TAP): 链接1 , 链接2 ,这将为您提供一个很好的编写代码模型,继续处理在等待您的结果时可以完成的事情运行动作直到它返回。 但是,我不确定这是否涵盖了您的所有需求。 特别是在Windows Workflow Foundation中,您可能希望查看某种forms的工作流hibernate/持久性 。

这种情况是使用WF持久性function的地方。 它允许您将工作流实例持久保存到数据库,以允许完成一些长时间运行的操作。 完成后,第二个线程或进程可以重新水合工作流实例并允许它恢复。

首先,您向工作流应用程序指定工作流实例存储 。 Microsoft提供了可以使用的SQL工作流实例存储实现 ,并提供了可以在SQL Server上运行的SQL脚本。

 namespace MySolution.MyWorkflowApp { using System.Activities; using System.Activities.DurableInstancing; using System.Activities.Statements; using System.Threading; internal static class Program { internal static void Main(string[] args) { var autoResetEvent = new AutoResetEvent(false); var workflowApp = new WorkflowApplication(new Sequence()); workflowApp.InstanceStore = new SqlWorkflowInstanceStore("server=mySqlServer;initial catalog=myWfDb;..."); workflowApp.Completed += e => autoResetEvent.Set(); workflowApp.Unloaded += e => autoResetEvent.Set(); workflowApp.Aborted += e => autoResetEvent.Set(); workflowApp.Run(); autoResetEvent.WaitOne(); } } } 

您的活动将启动将实际执行保存操作的辅助进程/线程。 您可以通过多种方式执行此操作:

  • 在辅助线程上
  • 通过异步调用Web方法实际上可以完成执行保存操作的繁重工作

您的活动如下所示:

 public sealed class WebSaveActivity : NativeActivity { public InArgument ObjectToSave { get; set; } protected override bool CanInduceIdle { get { // This notifies the WF engine that the activity can be unloaded / persisted to an instance store. return true; } } protected override void Execute(NativeActivityContext context) { var currentBigObject = this.ObjectToSave.Get(context); currentBigObject.WorkflowInstanceId = context.WorkflowInstanceId; StartSaveOperationAsync(this.ObjectToSave.Get(context)); // This method should offload the actual save process to a thread or even a web method, then return immediately. // This tells the WF engine that the workflow instance can be suspended and persisted to the instance store. context.CreateBookmark("MySaveOperation", AfterSaveCompletesCallback); } private void AfterSaveCompletesCallback(NativeActivityContext context, Bookmark bookmark, object value) { // Do more things after the save completes. var saved = (bool) value; if (saved) { // yay! } else { // boo!!! } } } 

书签创建向WF引擎发出信号,工作流实例可以从内存中卸载,直到唤醒工作流实例。

在您的方案中,您希望在长保存操作完成后恢复工作流。 让我们假设StartSaveOperationAsync方法将一条小消息写入某种队列,第二个线程或进程轮询执行保存操作:

 public static void StartSaveOperationAsync(MyBigObject myObjectToSave) { var targetQueue = new MessageQueue(".\private$\pendingSaveOperations"); var message = new Message(myObjectToSave); targetQueue.Send(message); } 

在我的第二个过程中,我可以轮询队列以获取新的保存请求并重新保存持久化的工作流实例,以便在保存操作完成后恢复。 假设以下方法位于不同的控制台应用程序中:

 internal static void PollQueue() { var targetQueue = new MessageQueue(@".\private$\pendingSaveOperations"); while (true) { // This waits for a message to arrive on the queue. var message = targetQueue.Receive(); var myObjectToSave = message.Body as MyBigObject; // Perform the long running save operation LongRunningSave(myObjectToSave); // Once the save operation finishes, you can resume the associated workflow. var autoResetEvent = new AutoResetEvent(false); var workflowApp = new WorkflowApplication(new Sequence()); workflowApp.InstanceStore = new SqlWorkflowInstanceStore("server=mySqlServer;initial catalog=myWfDb;..."); workflowApp.Completed += e => autoResetEvent.Set(); workflowApp.Unloaded += e => autoResetEvent.Set(); workflowApp.Aborted += e => autoResetEvent.Set(); // I'm assuming the object to save has a field somewhere that refers the workflow instance that's running it. workflowApp.Load(myObjectToSave.WorkflowInstanceId); workflowApp.ResumeBookmark("LongSaveOperation", true); // The 'true' parameter is just our way of saying the save completed successfully. You can use any object type you desire here. autoResetEvent.WaitOne(); } } private static void LongRunningSave(object myObjectToSave) { throw new NotImplementedException(); } public class MyBigObject { public Guid WorkflowInstanceId { get; set; } = Guid.NewGuid(); } 

现在,长时间运行的保存操作不会妨碍工作流引擎,并且通过不将工作流实例长时间保留在内存中,它将更有效地利用系统资源。