[WF4实践二] 使用 WorkflowInvoker 和 WorkflowApplication

Windows Workflow Foundation (WF) 提供承载工作流的若干方法。

WorkflowInvoker 提供调用工作流的简单方法,就像方法调用一样,并且只可用于不使用持久性的工作流。

WorkflowApplication 为执行工作流提供更丰富的模型,包括生命周期事件通知、执行控制、书签恢复和持久性。

WorkflowServiceHost 为消息传递活动提供支持,主要用于工作流服务。

使用 WorkflowInvoker

WorkflowInvoker 提供一种执行工作流的模型,如同使用方法调用。若要使用 WorkflowInvoker 调用工作流,请调用Invoke 方法,并传入要调用的工作流的工作流定义。在本示例中,使用 WorkflowInvoker 调用 WriteLine 活动。


   
   
  1. Activity wf = new WriteLine
  2. {
  3. Text = "Hello World."
  4. };
  5. WorkflowInvoker.Invoke(wf);

使用 WorkflowInvoker 调用工作流时,该工作流在调用线程上执行,并且在该工作流完成之前(包括任何空闲时间)会阻止Invoke 方法。若要配置一个工作流必须在其间完成的超时间隔,请使用一个采用 TimeSpan 参数的 Invoke 重载。在本示例中,将使用两个不同的超时间隔来调用两次工作流。第一个工作流完成,但第二个工作流未完成。


   
   
  1. Activity wf = new Sequence()
  2. {
  3. Activities =
  4. {
  5. new WriteLine()
  6. {
  7. Text = "Before the 1 minute delay."
  8. },
  9. new Delay()
  10. {
  11. Duration = TimeSpan.FromMinutes(1)
  12. },
  13. new WriteLine()
  14. {
  15. Text = "After the 1 minute delay."
  16. }
  17. }
  18. };
  19. // This workflow completes successfully.
  20. WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));
  21. // This workflow does not complete and a TimeoutException
  22. // is thrown.
  23. try
  24. {
  25. WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
  26. }
  27. catch (TimeoutException ex)
  28. {
  29. Console.WriteLine(ex.Message);
  30. }
Dd560894.note(zh-cn,VS.100).gif注意:
仅在达到超时间隔且工作流在执行期间进入空闲状态时才会引发 TimeoutException。如果工作流未进入空闲状态,那么完成时间超过指定超时间隔的工作流将会成功完成。

设置工作流的输入实参

使用输入形参字典可将数据传入工作流,其中实参名作为键,并映射到工作流的输入实参。在本示例中,将要调用 WriteLine,并使用输入形参字典指定其Text 实参值。


    
    
  1. Activity wf = new WriteLine();
  2. Dictionary<string, object> inputs = new Dictionary<string, object>();
  3. inputs.Add("Text", "Hello World.");
  4. WorkflowInvoker.Invoke(wf, inputs);

检索工作流的输出实参

使用调用 Invoke 所返回的输出字典,可获得工作流的输出形参。下面的示例调用一个工作流,该工作流包含一个带有两个输入参数和两个输出参数的Divide 活动。调用工作流时,会传递包含每个输入参数的值的 arguments 字典(由参数名键控)。当对Invoke 的调用返回时,将在 outputs 字典中返回每个输出参数(也由参数名键控)。


    
    
  1. public sealed class Divide : CodeActivity
  2. {
  3. [RequiredArgument]
  4. public InArgument<int> Dividend { get; set; }
  5. [RequiredArgument]
  6. public InArgument<int> Divisor { get; set; }
  7. public OutArgument<int> Remainder { get; set; }
  8. public OutArgument<int> Result { get; set; }
  9. protected override void Execute(CodeActivityContext context)
  10. {
  11. int quotient = Dividend.Get(context) / Divisor.Get(context);
  12. int remainder = Dividend.Get(context) % Divisor.Get(context);
  13. Result.Set(context, quotient);
  14. Remainder.Set(context, remainder);
  15. }
  16. }

    
    
  1. int dividend = 500;
  2. int divisor = 36;
  3. Dictionary<string, object> arguments = new Dictionary<string, object>();
  4. arguments.Add("Dividend", dividend);
  5. arguments.Add("Divisor", divisor);
  6. IDictionary<string, object> outputs =
  7. WorkflowInvoker.Invoke(new Divide(), arguments);
  8. Console.WriteLine("{0} / {1} = {2} Remainder {3}",
  9. dividend, divisor, outputs["Result"], outputs["Remainder"]);

如果工作流派生自 ActivityWithResult(例如 CodeActivity<TResult>Activity<TResult>),并且除了正确定义的Result 输出参数之外还有其他输出参数,则必须使用Invoke 的非泛型重载来检索其他参数。为此,传递给 Invoke 的工作流定义必须为 Activity 类型。在此示例中,Divide 活动派生自 CodeActivity<int>,但被声明为Activity。因此,将使用 Invoke 的非泛型重载,并返回参数字典,而不是单个返回值。


    
    
  1. public sealed class Divide : CodeActivity<int>
  2. {
  3. public InArgument<int> Dividend { get; set; }
  4. public InArgument<int> Divisor { get; set; }
  5. public OutArgument<int> Remainder { get; set; }
  6. protected override int Execute(CodeActivityContext context)
  7. {
  8. int quotient = Dividend.Get(context) / Divisor.Get(context);
  9. int remainder = Dividend.Get(context) % Divisor.Get(context);
  10. Remainder.Set(context, remainder);
  11. return quotient;
  12. }
  13. }

    
    
  1. int dividend = 500;
  2. int divisor = 36;
  3. Dictionary<string, object> arguments = new Dictionary<string, object>();
  4. arguments.Add("Dividend", dividend);
  5. arguments.Add("Divisor", divisor);
  6. Activity wf = new Divide();
  7. IDictionary<string, object> outputs =
  8. WorkflowInvoker.Invoke(wf, arguments);
  9. Console.WriteLine("{0} / {1} = {2} Remainder {3}",
  10. dividend, divisor, outputs["Result"], outputs["Remainder"]);

使用 WorkflowApplication

WorkflowApplication 为工作流实例管理提供了丰富的功能集。WorkflowApplication 承担实际WorkflowInstance 的线程安全代理任务,可封装运行时,并提供若干方法,用于创建和加载工作流实例、暂停与继续、终止和通知生命周期事件。若要使用WorkflowApplication 运行工作流,应创建 WorkflowApplication,订阅所需的所有生命周期事件,启动工作流,然后等待其完成。在本示例中,将要创建由WriteLine 活动组成的工作流定义,并使用指定工作流定义创建 WorkflowApplication。处理Completed 以便在工作流完成时通知宿主,通过调用 Run 启动工作流,然后宿主等待工作流完成。工作流完成时,设置 AutoResetEvent,并且宿主应用程序可以继续执行,如下例所示。


   
   
  1. AutoResetEvent syncEvent = new AutoResetEvent(false);
  2. Activity wf = new WriteLine
  3. {
  4. Text = "Hello World."
  5. };
  6. // Create the WorkflowApplication using the desired
  7. // workflow definition.
  8. WorkflowApplication wfApp = new WorkflowApplication(wf);
  9. // Handle the desired lifecycle events.
  10. wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
  11. {
  12. syncEvent.Set();
  13. };
  14. // Start the workflow.
  15. wfApp.Run();
  16. // Wait for Completed to arrive and signal that
  17. // the workflow is complete.
  18. syncEvent.WaitOne();

WorkflowApplication 生命周期事件

除了 Completed,当卸载工作流(Unloaded)、中止工作流(Aborted)、工作流变空闲(Idle PersistableIdle)或产生未经处理的异常(OnUnhandledException)时,宿主作者会收到通知。工作流应用程序开发人员可处理这些通知,并执行适当的操作,如下例所示。


    
    
  1. wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
  2. {
  3. if (e.CompletionState == ActivityInstanceState.Faulted)
  4. {
  5. Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
  6. Console.WriteLine("Exception: {0}\n{1}",
  7. e.TerminationException.GetType().FullName,
  8. e.TerminationException.Message);
  9. }
  10. else if (e.CompletionState == ActivityInstanceState.Canceled)
  11. {
  12. Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
  13. }
  14. else
  15. {
  16. Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
  17. // Outputs can be retrieved from the Outputs dictionary,
  18. // keyed by argument name.
  19. // Console.WriteLine("The winner is {0}.", e.Outputs["Winner"]);
  20. }
  21. };
  22. wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
  23. {
  24. // Display the exception that caused the workflow
  25. // to abort.
  26. Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
  27. Console.WriteLine("Exception: {0}\n{1}",
  28. e.Reason.GetType().FullName,
  29. e.Reason.Message);
  30. };
  31. wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
  32. {
  33. // Perform any processing that should occur
  34. // when a workflow goes idle. If the workflow can persist,
  35. // both Idle and PersistableIdle are called in that order.
  36. Console.WriteLine("Workflow {0} Idle.", e.InstanceId);
  37. };
  38. wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
  39. {
  40. // Instruct the runtime to persist and unload the workflow.
  41. // Choices are None, Persist, and Unload.
  42. return PersistableIdleAction.Unload;
  43. };
  44. wfApp.Unloaded = delegate(WorkflowApplicationEventArgs e)
  45. {
  46. Console.WriteLine("Workflow {0} Unloaded.", e.InstanceId);
  47. };
  48. wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
  49. {
  50. // Display the unhandled exception.
  51. Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
  52. e.InstanceId, e.UnhandledException.Message);
  53. Console.WriteLine("ExceptionSource: {0} - {1}",
  54. e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
  55. // Instruct the runtime to terminate the workflow.
  56. // Other choices are Abort and Cancel. Terminate
  57. // is the default if no OnUnhandledException handler
  58. // is present.
  59. return UnhandledExceptionAction.Terminate;
  60. };

设置工作流的输入实参

启动工作流时可使用形参字典将数据传入工作流,这与使用 WorkflowInvoker 时传入数据的方式类似。字典中的每一项都映射到指定工作流的一个输入实参。在本示例中,将要调用由WriteLine 活动组成的工作流,并使用输入形参字典指定其 Text 实参。


    
    
  1. AutoResetEvent syncEvent = new AutoResetEvent(false);
  2. Activity wf = new WriteLine();
  3. // Create the dictionary of input parameters.
  4. Dictionary<string, object> inputs = new Dictionary<string, object>();
  5. inputs.Add("Text", "Hello World!");
  6. // Create the WorkflowApplication using the desired
  7. // workflow definition and dictionary of input parameters.
  8. WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);
  9. // Handle the desired lifecycle events.
  10. wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
  11. {
  12. syncEvent.Set();
  13. };
  14. // Start the workflow.
  15. wfApp.Run();
  16. // Wait for Completed to arrive and signal that
  17. // the workflow is complete.
  18. syncEvent.WaitOne();

检索工作流的输出实参

当工作流完成时,可通过访问 System.Activities.WorkflowApplicationCompletedEventArgs.Outputs 字典在 Completed 处理程序中检索任何输出实参。下面的示例使用WorkflowApplication 承载一个工作流。该示例使用包含单个 DiceRoll 活动的工作流定义构造一个WorkflowApplication 实例。DiceRoll 活动包含两个表示掷骰子操作结果的输出参数。当工作流完成时,将在Completed 处理程序中检索输出。


    
    
  1. public sealed class DiceRoll : CodeActivity
  2. {
  3. public OutArgument<int> D1 { get; set; }
  4. public OutArgument<int> D2 { get; set; }
  5. static Random r = new Random();
  6. protected override void Execute(CodeActivityContext context)
  7. {
  8. D1.Set(context, r.Next(1, 7));
  9. D2.Set(context, r.Next(1, 7));
  10. }
  11. }

    
    
  1. // Create a WorkflowApplication instance.
  2. WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());
  3. // Subscribe to any desired workflow lifecycle events.
  4. wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
  5. {
  6. if (e.CompletionState == ActivityInstanceState.Faulted)
  7. {
  8. Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
  9. Console.WriteLine("Exception: {0}\n{1}",
  10. e.TerminationException.GetType().FullName,
  11. e.TerminationException.Message);
  12. }
  13. else if (e.CompletionState == ActivityInstanceState.Canceled)
  14. {
  15. Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
  16. }
  17. else
  18. {
  19. Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
  20. // Outputs can be retrieved from the Outputs dictionary,
  21. // keyed by argument name.
  22. Console.WriteLine("The two dice are {0} and {1}.",
  23. e.Outputs["D1"], e.Outputs["D2"]);
  24. }
  25. };
  26. // Run the workflow.
  27. wfApp.Run();
Dd560894.note(zh-cn,VS.100).gif注意:
WorkflowApplicationWorkflowInvoker 采用输入实参字典,并返回out 实参的字典。这些字典形参、属性及返回值的类型为 IDictionary<string, object>。传入的字典类的实际实例可以是实现了IDictionary<string, object> 的任何类。在这些示例中使用了 Dictionary<string, object>有关字典的更多信息,请参见IDictionaryDictionary

使用书签将数据传入运行中的工作流

书签是使活动能够被动等待继续的机制,也是将数据传入运行中的工作流实例的机制。如果某个活动在等待数据,它可以创建 Bookmark,并注册一个在继续 Bookmark 时要调用的回调方法,如下例所示。


    
    
  1. public sealed class ReadLine : NativeActivity<string>
  2. {
  3. [RequiredArgument]
  4. public InArgument<string> BookmarkName { get; set; }
  5. protected override void Execute(NativeActivityContext context)
  6. {
  7. // Create a Bookmark and wait for it to be resumed.
  8. context.CreateBookmark(BookmarkName.Get(context),
  9. new BookmarkCallback(OnResumeBookmark));
  10. }
  11. // NativeActivity derived activities that do asynchronous operations by calling
  12. // one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
  13. // must override the CanInduceIdle property and return true.
  14. protected override bool CanInduceIdle
  15. {
  16. get { return true; }
  17. }
  18. public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
  19. {
  20. // When the Bookmark is resumed, assign its value to
  21. // the Result argument.
  22. Result.Set(context, (string)obj);
  23. }

执行时,ReadLine 活动创建 Bookmark,注册回调,然后等待 Bookmark 继续。继续后,ReadLine 活动将随Bookmark 传递的数据赋给其 Result 实参。在本示例中,将要创建一个工作流,该工作流使用ReadLine 活动来收集用户名称并将其显示在控制台窗口中。


    
    
  1. Variable<string> name = new Variable<string>();
  2. Activity wf = new Sequence
  3. {
  4. Variables = { name },
  5. Activities =
  6. {
  7. new WriteLine
  8. {
  9. Text = "What is your name?"
  10. },
  11. new ReadLine
  12. {
  13. BookmarkName = "UserName",
  14. Result = new OutArgument<string>(name)
  15. },
  16. new WriteLine
  17. {
  18. Text = new InArgument<string>((env) =>
  19. ("Hello, " + name.Get(env)))
  20. }
  21. }
  22. };
  23. // Create a WorkflowApplication instance.
  24. WorkflowApplication wfApp = new WorkflowApplication(wf);
  25. // Workflow lifecycle events omitted except idle.
  26. AutoResetEvent idleEvent = new AutoResetEvent(false);
  27. wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
  28. {
  29. idleEvent.Set();
  30. };
  31. // Run the workflow.
  32. wfApp.Run();
  33. // Wait for the workflow to go idle before gathering
  34. // the user's input.
  35. idleEvent.WaitOne();
  36. // Gather the user's input and resume the bookmark.
  37. // Bookmark resumption only occurs when the workflow
  38. // is idle. If a call to ResumeBookmark is made and the workflow
  39. // is not idle, ResumeBookmark blocks until the workflow becomes
  40. // idle before resuming the bookmark.
  41. BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
  42. Console.ReadLine());
  43. // Possible BookmarkResumptionResult values:
  44. // Success, NotFound, or NotReady
  45. 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

    
    
  1. Variable<string> name = new Variable<string>();
  2. Activity wf = new Sequence
  3. {
  4. Variables = { name },
  5. Activities =
  6. {
  7. new WriteLine
  8. {
  9. Text = "What is your name?"
  10. },
  11. new ReadLine
  12. {
  13. BookmarkName = "UserName",
  14. Result = new OutArgument<string>(name)
  15. },
  16. new WriteLine
  17. {
  18. Text = new InArgument<string>((env) =>
  19. ("Hello, " + name.Get(env)))
  20. }
  21. }
  22. };
  23. // Create a WorkflowApplication instance.
  24. WorkflowApplication wfApp = new WorkflowApplication(wf);
  25. // Workflow lifecycle events omitted except idle.
  26. AutoResetEvent idleEvent = new AutoResetEvent(false);
  27. wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
  28. {
  29. // You can also inspect the bookmarks from the Idle handler
  30. // using e.Bookmarks
  31. idleEvent.Set();
  32. };
  33. // Run the workflow.
  34. wfApp.Run();
  35. // Wait for the workflow to go idle and give it a chance
  36. // to create the Bookmark.
  37. idleEvent.WaitOne();
  38. // Inspect the bookmarks
  39. foreach (BookmarkInfo info in wfApp.GetBookmarks())
  40. {
  41. Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
  42. info.BookmarkName, info.OwnerDisplayName);
  43. }
  44. // Gather the user's input and resume the bookmark.
  45. wfApp.ResumeBookmark("UserName", Console.ReadLine());

下面的代码示例检查传递给 WorkflowApplication 实例的 Idle 处理程序的WorkflowApplicationIdleEventArgs。在此示例中,进入空闲状态的工作流包含一个由名为 ReadInt 的活动所拥有的名为EnterGuessBookmark。此代码示例基于入门教程中的如何:运行工作流。如果修改了该阶段中的Idle 处理程序以包含此示例中的代码,则将显示以下输出。

BookmarkName: EnterGuess - OwnerDisplayName: ReadInt

    
    
  1. wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
  2. {
  3. foreach (BookmarkInfo info in e.Bookmarks)
  4. {
  5. Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
  6. info.BookmarkName, info.OwnerDisplayName);
  7. }
  8. idleEvent.Set();
  9. };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值