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

问题描述:

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

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

我应该如何改写这项活动,以满足长期运行活动的要求

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

编辑:

基于您的评论你可以看看基于任务的异步模式(TAP):Link 1Link 2这将使你编写代码一个很好的模式,继续在事情是可以做的工作同时等待长时间运行的结果,直到它返回。但是,我不确定这是否涵盖了您的所有需求。在Windows Workflow Foundation中,您可能需要查看某种形式的workflow hibernation/persistence

+0

不,我希望工作流被暂停(或进入空闲模式)直到'WebSave'完成,因此在接下来的活动中我可以使用结果。 – mehrandvd

这种情况下,使用WF的持久性功能发光。它允许您将工作流实例持久化到数据库,以允许完成一些长时间运行的操作。一旦完成,第二个线程或进程可以重新提供工作流程实例并使其恢复。

首先您向工作流应用程序指定一个工作流实例存储区。 Microsoft提供了一个您可以使用的SQL workflow instance store implementation,并提供了可以在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<MyBigObject> 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引擎,工作流实例可以从内存中卸载直到某些东西被唤醒e工作流实例。

在您的方案中,您希望工作流程在长时间保存操作完成后恢复。让我们假设该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(); 
} 

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

+0

谢谢你的解决方案。但我的问题是我没有访问工作流服务器代码,我想处理我的活动中的所有代码,而不是通过改变类似于“WorkflowApplication” – mehrandvd