共用方式為


使用 WorkflowInvoker 和 WorkflowApplication

Windows Workflow Foundation (WF) 提供數種方法來裝載工作流程。 WorkflowInvoker 提供簡單的方法來叫用工作流程,就像是方法呼叫一樣,而且只能用於不使用持續性的工作流程。 WorkflowApplication 提供更豐富的模型來執行工作流程,其中包含生命週期事件的通知、執行控件、書籤繼續和持續性。 WorkflowServiceHost 提供傳訊活動的支援,且主要與工作流程服務搭配使用。 本主題將介紹使用WorkflowInvokerWorkflowApplication進行托管的工作流程。 如需使用 WorkflowServiceHost裝載工作流程的詳細資訊,請參閱工作流程服務和裝載工作流程服務概觀

使用 WorkflowInvoker

WorkflowInvoker 提供模型來執行工作流程,就像是方法呼叫一樣。 若要叫用工作流程,請呼叫 WorkflowInvoker 方法,並傳入要叫用的工作流程定義。 在此範例中,使用WriteLine來叫用WorkflowInvoker活動。

Activity wf = new WriteLine
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

使用 叫用 WorkflowInvoker工作流程時,工作流程會在呼叫線程上執行,方法 Invoke 會封鎖直到工作流程完成為止,包括任何空閒時間。 若要設定工作流程必須完成的超時時間間隔,請使用採用 參數的 Invoke 其中一個 TimeSpan 多載。 在此範例中,會使用兩個不同的超時時間間隔叫用工作流程兩次。 第一個工作流程會完成,但第二個工作流程不會完成。

Activity wf = new Sequence()
{
    Activities =
    {
        new WriteLine()
        {
            Text = "Before the 1 minute delay."
        },
        new Delay()
        {
            Duration = TimeSpan.FromMinutes(1)
        },
        new WriteLine()
        {
            Text = "After the 1 minute delay."
        }
    }
};

// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));

// This workflow does not complete and a TimeoutException
// is thrown.
try
{
    WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
    Console.WriteLine(ex.Message);
}

備註

TimeoutException只有在逾時間隔經過且工作流程在執行期間變成閑置時,才會擲回 。 如果工作流程未閑置,花費的時間超過指定的超時時間間隔才能順利完成的工作流程。

WorkflowInvoker 也提供叫用方法的異步版本。 如需詳細資訊,請參閱 InvokeAsyncBeginInvoke

設定工作流程的輸入自變數

數據可以使用輸入參數的字典,以自變數名稱為索引鍵,對應至工作流程的輸入自變數,將數據傳遞至工作流程。 在此範例中, WriteLine 會叫用 ,並使用輸入參數的字典來指定其 Text 自變數的值。

Activity wf = new WriteLine();

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");

WorkflowInvoker.Invoke(wf, inputs);

擷取工作流程的輸出自變數

工作流程的輸出參數可以使用從呼叫 Invoke傳回的 outputs 字典來取得。 下列範例會叫用由具有兩個輸入自變數和兩個輸出自變數的單 Divide 一活動所組成的工作流程。 叫用工作流程時, arguments 會傳遞字典,其中包含每個輸入自變數的值,以自變數名稱為索引鍵。 當呼叫 Invoke 傳回時,字典中 outputs 會傳回每個輸出自變數,也會以自變數名稱為索引鍵。

public sealed class Divide : CodeActivity
{
    [RequiredArgument]
    public InArgument<int> Dividend { get; set; }

    [RequiredArgument]
    public InArgument<int> Divisor { get; set; }

    public OutArgument<int> Remainder { get; set; }
    public OutArgument<int> Result { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Result.Set(context, quotient);
        Remainder.Set(context, remainder);
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(new Divide(), arguments);

Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

如果工作流程衍生自 ActivityWithResult,例如 CodeActivity<TResult>Activity<TResult>,而且除了明確定義的 Result 輸出引數之外,還有其他輸出引數,則必須使用 Invoke 的非泛型多載來擷取這些額外引數。 若要這樣做,傳入 Invoke 的工作流程定義必須是 類型 Activity。 在此範例中, Divide 活動衍生自 CodeActivity<int>,但宣告為 Activity ,因此會使用 的非泛型多載 Invoke ,以傳回自變數的字典,而不是單一傳回值。

public sealed class Divide : CodeActivity<int>
{
    public InArgument<int> Dividend { get; set; }
    public InArgument<int> Divisor { get; set; }
    public OutArgument<int> Remainder { get; set; }

    protected override int Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Remainder.Set(context, remainder);

        return quotient;
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

Activity wf = new Divide();

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(wf, arguments);

Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

使用 WorkflowApplication

WorkflowApplication 提供一組豐富的工作流程實例管理功能。 WorkflowApplication 做為實際 WorkflowInstance的線程安全 Proxy,其封裝運行時間,並提供建立和載入工作流程實例、暫停和繼續、終止和生命週期事件通知的方法。 若要使用 WorkflowApplication 您建立 WorkflowApplication執行工作流程,請訂閱任何所需的生命週期事件、啟動工作流程,然後等候工作流程完成。 在此範例中,首先建立由 WriteLine 活動組成的工作流程定義,然後使用該指定的工作流程定義來建立 WorkflowApplicationCompleted 已處理,因此當工作流程完成時,主機會收到通知,工作流程會以呼叫 Run啟動,然後主機等候工作流程完成。 當工作流程完成時, AutoResetEvent 會設定 ,而且主應用程式可以繼續執行,如下列範例所示。

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine
{
    Text = "Hello World."
};

// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

WorkflowApplication 生命週期事件

此外Completed,當工作流程卸除(Unloaded)、中止(Aborted)、變成閒置(IdlePersistableIdle)或出現未處理的例外狀況(OnUnhandledException)時,主機作者也可以收到通知。 工作流程應用程式開發人員可以處理這些通知並採取適當的動作,如下列範例所示。

wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        // Console.WriteLine($"The winner is {e.Outputs["Winner"]}.");
    }
};

wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
    Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{e.Reason.Message}");
};

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // Perform any processing that should occur
    // when a workflow goes idle. If the workflow can persist,
    // both Idle and PersistableIdle are called in that order.
    Console.WriteLine($"Workflow {e.InstanceId} Idle.");
};

wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // Instruct the runtime to persist and unload the workflow.
    // Choices are None, Persist, and Unload.
    return PersistableIdleAction.Unload;
};

wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
{
    Console.WriteLine($"Workflow {e.InstanceId} Unloaded.");
};

wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");

    Console.WriteLine($"ExceptionSource: {e.ExceptionSource.DisplayName} - {e.ExceptionSourceInstanceId}");

    // Instruct the runtime to terminate the workflow.
    // Other choices are Abort and Cancel. Terminate
    // is the default if no OnUnhandledException handler
    // is present.
    return UnhandledExceptionAction.Terminate;
};

設定工作流程的輸入自變數

數據可以傳入工作流程,因為數據是使用參數的字典來啟動,類似於使用 WorkflowInvoker時傳入數據的方式。 字典中的每個項目都會對應至指定工作流程的輸入自變數。 在此範例中,會叫用由活動組成的 WriteLine 工作流程,並使用輸入參數的字典來指定其 Text 自變數。

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine();

// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");

// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);

// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

擷取工作流程的輸出自變數

工作流程完成時,可以藉由存取Completed字典,在處理程式中WorkflowApplicationCompletedEventArgs.Outputs擷取任何輸出自變數。 下列範例使用 WorkflowApplication裝載工作流程。 WorkflowApplication實例是使用由單DiceRoll一活動組成的工作流程定義來建構。 活動 DiceRoll 有兩個輸出自變數,代表骰子滾滾作業的結果。 工作流程完成時,會在處理程式中 Completed 擷取輸出。

public sealed class DiceRoll : CodeActivity
{
    public OutArgument<int> D1 { get; set; }
    public OutArgument<int> D2 { get; set; }

    static Random r = new Random();

    protected override void Execute(CodeActivityContext context)
    {
        D1.Set(context, r.Next(1, 7));
        D2.Set(context, r.Next(1, 7));
    }
}
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());

// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        Console.WriteLine($"The two dice are {e.Outputs["D1"]} and {e.Outputs["D2"]}.");
    }
};

// Run the workflow.
wfApp.Run();

備註

WorkflowApplicationWorkflowInvoker 接受輸入自變數的字典,並傳回自變數的 out 字典。 這些字典參數、屬性和傳回值的類型為 IDictionary<string, object>。 傳入之字典類別的實際實例可以是任何實作 的 IDictionary<string, object>類別。 在這些範例中, Dictionary<string, object> 會使用 。 如需字典的詳細資訊,請參閱 IDictionary<TKey,TValue>Dictionary<TKey,TValue>

使用書籤將數據傳遞至執行中的工作流程

書籤是活動可以被動等候繼續的機制,而且是將數據傳遞至執行中工作流程實例的機制。 如果某個活動正在等待資料,它可以建立Bookmark,並註冊一個回呼方法,當Bookmark恢復時調用該方法,如下列範例所示。

public sealed class ReadLine : NativeActivity<string>
{
    [RequiredArgument]
    public InArgument<string> BookmarkName { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        // Create a Bookmark and wait for it to be resumed.
        context.CreateBookmark(BookmarkName.Get(context),
            new BookmarkCallback(OnResumeBookmark));
    }

    // NativeActivity derived activities that do asynchronous operations by calling
    // one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
    // must override the CanInduceIdle property and return true.
    protected override bool CanInduceIdle
    {
        get { return true; }
    }

    public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
    {
        // When the Bookmark is resumed, assign its value to
        // the Result argument.
        Result.Set(context, (string)obj);
    }

執行時,ReadLine 活動會建立 Bookmark,註冊回呼,然後等候 Bookmark 恢復。 在恢復時,ReadLine 活動會將與 Bookmark 一起傳遞的數據指派給其 Result 參數。 在此範例中,會建立工作流程, ReadLine 該工作流程會使用 活動來收集用戶的名稱,並將其顯示至控制台視窗。

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();

// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
    Console.ReadLine());

// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine($"BookmarkResumptionResult: {result}");

ReadLine活動執行時,它會建立一個Bookmark名為UserName,然後等候書籤繼續。 主機會收集所需的數據,然後繼續 Bookmark。 工作流程會繼續、顯示名稱,然後完成。

主應用程式可以檢查工作流程,以判斷是否有任何作用中的書籤。 它可以藉由呼叫 GetBookmarks 實例的 WorkflowApplication 方法,或藉由檢查 WorkflowApplicationIdleEventArgs 處理程式中的 Idle 來執行此動作。

下列程式代碼範例與上一個範例類似,不同之處在於繼續書籤之前會列舉使用中的書籤。 工作流程已啟動;一旦 Bookmark 建立且工作流程暫停後,GetBookmarks 就會被呼叫。 工作流程完成時,主控台會顯示下列輸出。

你叫什麼名字?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHello, Steve

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // You can also inspect the bookmarks from the Idle handler
    // using e.Bookmarks

    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();

// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
    Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}

// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());

下列程式碼範例會檢查傳遞至 WorkflowApplicationIdleEventArgs 處理器的 Idle 實例中的 WorkflowApplication。 在此範例中,閒置的工作流程中包含一個名為BookmarkEnterGuess,這是由名為ReadInt的活動所擁有。 此程式代碼範例是以 如何:執行工作流程為基礎,這是 用戶入門教學課程的一部分。 Idle如果該步驟中的處理程式已修改為包含此範例中的程式代碼,則會顯示下列輸出。

BookmarkName:EnterGuess - OwnerDisplayName: ReadInt

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    foreach (BookmarkInfo info in e.Bookmarks)
    {
        Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
    }

    idleEvent.Set();
};

總結

WorkflowInvoker 提供一種輕量的方式來調用工作流程,雖然它提供了在工作流程開始時用來傳入數據的方法,以及從已完成的工作流程中提取數據的方法,但它並不適用於更複雜的場景,在那種情況下可使用 WorkflowApplication