Windows Workflow Foundation (WF) 提供數種方法來裝載工作流程。 WorkflowInvoker 提供簡單的方法來叫用工作流程,就像是方法呼叫一樣,而且只能用於不使用持續性的工作流程。 WorkflowApplication 提供更豐富的模型來執行工作流程,其中包含生命週期事件的通知、執行控件、書籤繼續和持續性。 WorkflowServiceHost 提供傳訊活動的支援,且主要與工作流程服務搭配使用。 本主題將介紹使用WorkflowInvoker和WorkflowApplication進行托管的工作流程。 如需使用 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 也提供叫用方法的異步版本。 如需詳細資訊,請參閱 InvokeAsync 和 BeginInvoke。
設定工作流程的輸入自變數
數據可以使用輸入參數的字典,以自變數名稱為索引鍵,對應至工作流程的輸入自變數,將數據傳遞至工作流程。 在此範例中, 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 活動組成的工作流程定義,然後使用該指定的工作流程定義來建立 WorkflowApplication。 Completed 已處理,因此當工作流程完成時,主機會收到通知,工作流程會以呼叫 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)、變成閒置(Idle 和 PersistableIdle)或出現未處理的例外狀況(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();
備註
WorkflowApplication 並 WorkflowInvoker 接受輸入自變數的字典,並傳回自變數的 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。 在此範例中,閒置的工作流程中包含一個名為Bookmark的EnterGuess
,這是由名為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。