共用方式為


使用 WorkflowInvoker 與 WorkflowApplication

本主題僅適用於 Windows Workflow Foundation 4。

Windows Workflow Foundation (WF) 提供幾種裝載工作流程的方法。WorkflowInvoker 提供如同方法呼叫般的叫用工作流程簡易方法,且僅適用於不使用持續性的工作流程。WorkflowApplication 提供更豐富的執行工作流程模式,包含開發週期事件的通知、執行控制、書籤繼續與持續性。WorkflowServiceHost 提供對訊息活動的支援,且主要用於工作流程服務。 本主題會向您介紹使用 WorkflowInvokerWorkflowApplication 進行裝載的工作流程。如需詳細資訊使用 WorkflowServiceHost 裝載工作流程的詳細資訊,請參閱工作流程服務裝載工作流程服務

使用 WorkflowInvoker

WorkflowInvoker如同方法叫用般,提供執行工作流程的模型。 若要使用 WorkflowInvoker 叫用工作流程,請呼叫 Invoke 方法,並傳入要叫用之工作流程的工作流程定義。 在此範例中,會使用 WorkflowInvoker 叫用 WriteLine 活動。

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

WorkflowInvoker.Invoke(wf);

使用 WorkflowInvoker 叫用工作流程時,工作流程會在呼叫執行緒上執行,且 Invoke 方法會封鎖至工作流程完成為止,並包含任何閒置時間。 若要設定工作流程必須完成的逾時間隔,請使用接受 TimeSpan 的其中一個 Invoke 多載。 在此範例中,會以兩個不同的逾時間隔叫用工作流程兩次。 第一個工作流程會完成,但是第二個則不會。

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);
}
Dd560894.note(zh-tw,VS.100).gif注意:
只有在超過逾時間隔及工作流程在執行期間變成閒置狀態時,才會擲回 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 呼叫傳回的輸出字典來取得。 下列範例會叫用由具有兩個輸入引數和兩個輸出引數之單一 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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], 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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], 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();

工作流程應用程式開發週期事件

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

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

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

wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
    Console.WriteLine("Exception: {0}\n{1}",
        e.Reason.GetType().FullName,
        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 {0} Idle.", e.InstanceId);
};

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 {0} Unloaded.", e.InstanceId);
};

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

    Console.WriteLine("ExceptionSource: {0} - {1}",
        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();

擷取工作流程的輸出引數

當工作流程完成時,可存取 System.Activities.WorkflowApplicationCompletedEventArgs.Outputs 字典來擷取 Completed 處理常式中的任何輸出引數。 下列範例會使用 WorkflowApplication 來裝載工作流程。 系統會使用由單一 DiceRoll 活動組成的工作流程定義來建構 WorkflowApplication 執行個體。 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 {0} Terminated.", e.InstanceId);
         Console.WriteLine("Exception: {0}\n{1}",
             e.TerminationException.GetType().FullName,
             e.TerminationException.Message);
     }
     else if (e.CompletionState == ActivityInstanceState.Canceled)
     {
         Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
     }
     else
     {
         Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

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

// Run the workflow.
 wfApp.Run();
Dd560894.note(zh-tw,VS.100).gif注意:
WorkflowApplicationWorkflowInvoker 會採用輸入參數的字典,並傳回 out 引數的字典。 這些字典參數、屬性與傳回值都屬於型別 IDictionary<string, object>。 傳入的字典類別實際執行個體,可以是任何實作 IDictionary<string, object> 的類別。 在這些範例中,會使用 Dictionary<string, object>如需詳細資訊。IDictionary字典,請參閱 Dictionary 與 。

使用書籤將資料傳入執行中的工作流程

書籤是一種機制,可讓活動被動等候繼續,也可將資料傳遞至執行中的工作流程執行個體。 如果活動正在等候資料,就可建立 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: {0}", result);

當執行 ReadLine 活動時,它會建立稱為 UserNameBookmark,然後等候書籤繼續。 主機會收集想要的資料,然後繼續 Bookmark。 工作流程會繼續、顯示名稱,然後完成。

主應用程式可以檢查工作流程,以判斷是否有任何作用中的書籤。 若要進行這項處理,它可以呼叫 WorkflowApplication 執行個體的 GetBookmarks 方法,或是在 Idle 處理常式中檢查 WorkflowApplicationIdleEventArgs

下列程式碼範例與上一個範例很相似,例外之處是先列舉作用中的書籤,然後再繼續書籤。 系統會啟動工作流程,而且一旦建立 Bookmark 並且工作流程處於閒置狀態之後,就會呼叫 GetBookmarks。 當工作流程完成時,主控台就會顯示下列輸出。

What is your name? 
BookmarkName: UserName - OwnerDisplayName: ReadLine
Steve
Hello, 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: {0} - OwnerDisplayName: {1}",
        info.BookmarkName, info.OwnerDisplayName);
}

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

下列程式碼範例會檢查傳遞給 WorkflowApplication 執行個體之 Idle 處理常式的 WorkflowApplicationIdleEventArgs。 在此範例中,處於閒置狀態的工作流程具有一個名為 EnterGuess 且由名為 ReadInt 之活動所擁有的 Bookmark。 這個程式碼範例是以 HOW TO:執行工作流程為基礎,而該說明主題屬於使用者入門教學課程的一部分。 如果該步驟中的 Idle 處理常式修改為包含此範例中的程式碼,就會顯示下列輸出。

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

    idleEvent.Set();
};

摘要

WorkflowInvoker 提供叫用工作流程的輕鬆方式,雖然它提供在工作流程之初傳入資料,以及從已完成之工作流程擷取資料的方法,但它並不提供可使用 WorkflowApplication 的更複雜案例。