0

私は次のことを達成しようとしています:

  1. カスタム アクティビティを含む長時間実行されるワークフローを開始します。
  2. これらのカスタム アクティビティは、ブックマークを作成してその再開を待つというパターンに従います。
  3. 開始されたワークフロー ID を返し、どこかに保存します。
  4. 後で、保存された ID からワークフローをロードします。
  5. ワークフローが完了したかどうかを確認し、完了していない場合は、ブロックされたブックマークがあるかどうかを確認します。
  6. これらのブックマークを再開すると、関連するアクティビティが終了し、最終的にワークフロー全体が終了します。

簡単な使用例は、ドキュメントをレビューして承認または却下するためのワークフローです。そのためのワークフローが作成され、個人に通知され、レビューを承認または拒否することで、必要なときにいつでもフィードバックを提供できます。

http://xhinker.com/post/WF4.aspxで入手できる Andrew Zhu のコードを例として使用しました。

私が抱えている問題は次のとおりです。

  1. 私が説明したようなカスタム アクティビティを使用すると、ワークフローの開始WaitForRunnableInstance時に無期限に待機するため、ワークフローが開始されてデータベースに永続化されたことは通知されません。
  2. ワークフローは実際に保存され、BlockingBookmarksカスタム アクティビティの ID に設定された列ExecutionStatus、アイドルにIsInitialized設定された列、1 にIsSuspended設定された列、0 にIsCompleted設定された列、0 に設定された列、0 に設定された列がありIsReadyToRunます。

私はすでにマイクロソフト フォーラムでディスカッションを開始しまし。 、しかし、まだ何かが正しくありません。

これに関するアイデアはありますか?カスタム アクティビティを使用して長時間実行されるワークフローに役立つパターンはありますか?

ありがとう!

4

1 に答える 1

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 内に実装するのに骨の折れる作業のうち 2 つ (持続性と監視) を処理します。このパスを選択した場合、次のコードを記述する必要はありません。

ワークフローを WCF アプリケーションとして展開し、他の WCF コントラクトとして呼び出すだけです。受信アクティビティ (時間がかかりすぎる場合に待機して持続するもの) であるOperationContractsと、対応する送信アクティビティ (クライアントに値を返すもの) があります。それらの間の相関の概念さえあります。AppFabric は、以前に初期化された関連付けハンドルを渡すだけで、ワークフローの再開を処理します。

AppFabric は、永続ストア、監視、およびアイドル状態や永続状態になるまでの時間などのその他のオプションを構成するための構成 UI を提供します。

Active/Idle/Suspended ワークフロー、監視データなどを可視化できます。

于 2012-09-11T14:53:57.653 に答える