2017-06-18 96 views
2

我創建了一個活動來執行Web請求並將結果存儲到數據庫中。我發現對於這些長時間運行的活動,我應該編寫一些不同的代碼,以便工作流引擎線程不會被阻塞。如何編寫長時間運行的活動來調用WF 4.0中的Web服務

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

我應該如何改寫這項活動,以滿足長期運行活動的要求

回答

1

你可以或者使用例如,您的現有流程中產生一個線程ThreadPool.QueueUserWorkItem()因此,如果需要,您的工作流程的其餘部分將繼續運行。請務必首先了解多線程和線程同步的含義。 或者您可以查看Hangfire或類似的組件,將整個作業卸載到不同的進程中。

編輯:

基於您的評論你可以看看基於任務的異步模式(TAP):Link 1Link 2這將使你編寫代碼一個很好的模式,繼續在事情是可以做的工作同時等待長時間運行的結果,直到它返回。但是,我不確定這是否涵蓋了您的所有需求。在Windows Workflow Foundation中,您可能需要查看某種形式的workflow hibernation/persistence

+0

不,我希望工作流被暫停(或進入空閒模式)直到'WebSave'完成,因此在接下來的活動中我可以使用結果。 – mehrandvd

0

這種情況下,使用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

相關問題