Windows Workflow Foundation (WF) 提供承载工作流的若干方法。
WorkflowInvoker 提供调用工作流的简单方法,就像方法调用一样,并且只可用于不使用持久性的工作流。
WorkflowApplication 为执行工作流提供更丰富的模型,包括生命周期事件通知、执行控制、书签恢复和持久性。
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);
- }
![]() |
---|
仅在达到超时间隔且工作流在执行期间进入空闲状态时才会引发 TimeoutException。如果工作流未进入空闲状态,那么完成时间超过指定超时间隔的工作流将会成功完成。 |
设置工作流的输入实参
使用输入形参字典可将数据传入工作流,其中实参名作为键,并映射到工作流的输入实参。在本示例中,将要调用 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 的线程安全代理任务,可封装运行时,并提供若干方法,用于创建和加载工作流实例、暂停与继续、终止和通知生命周期事件。若要使用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 {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();
![]() |
---|
WorkflowApplication 和 WorkflowInvoker 采用输入实参字典,并返回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
活动时,该活动创建一个名为 UserName
的 Bookmark,然后等待书签继续。宿主收集所需的数据,然后继续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。在此示例中,进入空闲状态的工作流包含一个由名为 ReadInt
的活动所拥有的名为EnterGuess
的 Bookmark。此代码示例基于入门教程中的如何:运行工作流。如果修改了该阶段中的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();
- };