0

我努力實現以下目標:長時間運行WF 4模式

  1. 啓動工作流可以去運行很長一段時間,其中包括自定義活動;
  2. 這些自定義活動將遵循創建書籤並等待其簡歷的模式;
  3. 返回開始的工作流ID並將其存儲在某個地方;
  4. 在稍後的時間點,從其存儲的ID加載工作流;
  5. 檢查工作流是否已完成,如果沒有,檢查是否有任何被阻止的書籤;
  6. 恢復這些書籤,從而導致相關活動完成,最終導致整個工作流程完成。

一個簡單的例子將是:審閱文檔,要麼批准它或拒絕它的工作流。將爲此創建一個工作流程,個人將被通知,並且只要他們想要,他們可以通過批准或拒絕審查來提供他們的反饋。

我以Andrew Zhu的代碼爲例,我的代碼是http://xhinker.com/post/WF4.aspx

的問題,我已經是:

  1. 當使用像我描述了一個自定義活動,一旦開始了工作流程中,WaitForRunnableInstance將無限期地等待,所以我從來沒有通知我的工作流程已經開始,並持續到數據庫;
  2. 工作流程確實存放,並具有列BlockingBookmarks設置爲我的自定義活動的ID,ExecutionStatus設置爲空閒,IsInitialized設置爲1,IsSuspended設置爲0,IsCompleted設置爲0,IsReadyToRun設置爲0

我已經開始討論微軟論壇,可以在http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/6262874d-3493-4be1-bd05-b990307e1875/看到並獲得一些反饋,但有些東西仍然是不正確的。

對此的任何想法?針對具有自定義活動的長時間運行工作流程的任

謝謝!

+0

有沒有什麼不能承載這些工作流程爲通過IIS服務的一個原因? –

+0

Mike: 謝謝。你會怎麼看?我會得到什麼好處?這可能是一種可能性,我不明白爲什麼,但另一方面,我也不明白爲什麼。 :-) –

回答

5

這通常是長時間運行的工作流的最小示例,它等待控制檯上的用戶輸入。 (此代碼永遠不會執行,把它作爲唯一的一個例子)

/// Activity that waits on bookmark for 
/// someone to send it some text 
/// 
public sealed class ReadLine: NativeActivity<string> 
{ 
    [RequiredArgument] 
    public InArgument<string> BookmarkName { get; set; } 

    protected override bool CanInduceIdle 
    { 
     get 
     { 
      return true; 
     } 
    } 

    protected override void Execute(NativeActivityContext context) 
    { 
     context.CreateBookmark(
      BookmarkName.Get(context), 
      new BookmarkCallback(OnReadComplete)); 
    } 

    void OnReadComplete(NativeActivityContext context, Bookmark bookmark, object state) 
    { 
     context.SetValue(base.Result, state as string); 
    } 
} 

/// Program that uses ReadLine activity's bookmark to persist 
/// workflow and waits for user input to resume it 
/// 
public class Program 
{ 
    static InstanceStore InstanceStore; 
    static Activity Activity = GetExampleActivity(); 
    static AutoResetEvent unloadEvent = new AutoResetEvent(false); 
    static Guid WfId; 
    static WorkflowApplication WfApp; 

    const string READ_LINE_BOOKMARK = "ReadLineBookMark"; 

    static void Main() 
    { 
     CreateInstanceStore(); 
     CreateWorkflowApp(); 

     // Start workflow application and wait for input 
     StartAndUnload(); 

     //Get user input and send it to ReadLine bookmark reviving workflow 
     GetInputAndComplete(); 
    } 

    static void StartAndUnload() 
    { 
     WfApp.Run(); 
     WfId = app.Id; 
     // !! Workflow will go idle on bookmark, no need to call Unload() 
     unloadEvent.WaitOne(); 
    } 

    static void GetInputAndComplete() 
    { 
     var input = Console.ReadLine(); 

     // We've the text input, let's resume this thing 
     WfApp.Load(WfId); 
     WfApp.ResumeBookmark(READ_LINE_BOOKMARK, input); 

     unloadEvent.WaitOne(); 
    } 

    static void CreateInstanceStore() 
    { 
     InstanceStore = new SqlWorkflowInstanceStore("connection string"); 

     var handle = InstanceStore.CreateInstanceHandle();    
     var view = InstanceStore.Execute(
      handle, 
      new CreateWorkflowOwnerCommand(), 
      TimeSpan.FromSeconds(5));    

     handle.Free(); 

     InstanceStore.DefaultInstanceOwner = view.InstanceOwner; 
    } 

    static void CreateWorkflowApp() 
    { 
     WfApp = new WorkflowApplication(Activity) 
     { 
      InstanceStore = InstanceStore, 
     }; 

     WfApp.PersistableIdle = (e) => { return PersistableIdleAction.Unload; } 
     WfApp.Unloaded = (e) => 
     { 
      Console.WriteLine("WF App Unloaded\n"); 
      unloadEvent.Set(); 
     }; 
     WfApp.Completed = (e) => 
     { 
      Console.WriteLine("\nWF App Ended: {0}.", e.CompletionState); 
     }; 
    } 

    static Activity GetExampleActivity() 
    { 
     var response = new Variable<string>(); 

     return return new Sequence() 
     { 
      Variables = { response }, 
      Activities = 
      { 
       new WriteLine() 
       { 
        Text = new InArgument<string>("Type some word:") 
       }, 
       new ReadLine() 
       { 
        BookmarkName = READ_LINE_BOOKMARK, 
        Result = new OutArgument<string>(response) 
       }, 
       new WriteLine() 
       { 
        Text = new InArgument<string>((context) => "You've typed: " + response.Get(context)) 
       } 
      } 
     }; 
    } 

話雖這麼說,請考慮使用IIS和AppFabric的,你會不會後悔。 AppFabric有六次點擊,在WF中實現了兩個必須要做的痛苦事情:持久性和監控。如果您選擇此路徑,您不會永遠不需要編寫代碼。

將您的工作流程部署爲WCF應用程序,並將其作爲任何其他WCF合同進行調用。你有OperationContracts這是接收活動(那些誰等待並堅持,如果它需要太長時間)和相應的發送活動(誰返回給客戶端)。你甚至有他們之間的相關性的概念。AppFabric負責恢復工作流,只是傳遞一個先前初始化的相關句柄。

AppFabric爲您提供了一個配置用戶界面來配置持久性存儲,監控和其他選項,如閒置和/或保持之前的時間。

您可以直觀的有功/無/暫停工作流程,監測數據,等等,等等

+0

Jota: 非常感謝!我不得不稍微修改你的代碼(在GetInputAndComplete上創建一個新的WorkflowApplication,否則會抱怨)。這與我的代碼非常相似,除了我正在等待工作流在創建時進入運行狀態。 –