[置顶]坚持学习WF文章索引

WF提供了一组核心服务,例如在SQL 数据库中存储工作流实例的执行详细信息的持久性服务,计划服务,事务服务和跟踪服务。除了这些WF也提供了另外一种服务,叫做Local Service也可以叫做Data exchange service。主要是实现工作流和宿主程序之间的通信,使工作流能够使用方法和事件通过消息与外部系统交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序。 通过事件与工作流进行通信的功能提供了一种将数据发送到工作流的异步方式。本文主要讲述调用外部方法的部分。

下图说明本地通信服务如何与其主机应用程序通信:

附件: 01_thumb.jpg

下面首先说说如何开发一个本地服务:

1.使用C#的接口定义服务契约,在接口中定义你方法和事件。并使用[ExternalDataExchangeAttribute]装饰该接口,用于说明这是一个本地服务的接口。
2.开发一个实现了该接口的类,用于实现你的逻辑。
3.创建一个工作流实例,并将该本地服务添加到工作流引擎中去。

我们开发一个简单的本地服务的例子,根据AccountID来修改Balance的值,并使用三种方式来调用:
1.定义一个Account类,代码如下(Account.cs)
  1. using System;
  2. namespace CaryWorkflows
  3. {
  4.     [Serializable]
  5.     public class Account
  6.     {
  7.         private Int32 _id;
  8.         private String _name = String.Empty;
  9.         private Double _balance;

  10.         public Int32 Id
  11.         {
  12.             get { return _id; }
  13.             set { _id = value; }
  14.         }
  15.         public String Name
  16.         {
  17.             get { return _name; }
  18.             set { _name = value; }
  19.         }
  20.         public Double Balance
  21.         {
  22.             get { return _balance; }
  23.             set { _balance = value; }
  24.         }
  25.     }
  26. }
复制代码
2.定义一个接口,需要ExternalDataExchange属性,代码如下(IAccountServices.cs):
  1. using System;
  2. using System.Workflow.Activities;
  3. namespace CaryWorkflows
  4. {
  5.     [ExternalDataExchange]
  6.     public interface IAccountServices
  7.     {
  8.         Account AdjustBalance(Int32 id, Double adjustment);
  9.     }
  10. }
复制代码
3.实现该接口,代码如下():
  1. using System;
  2. using System.Collections.Generic;
  3. namespace CaryWorkflows
  4. {
  5.     public class AccountService : IAccountServices
  6.     {
  7.         private Dictionary<Int32, Account> _accounts= new Dictionary<int, Account>();
  8.         public AccountService()
  9.         {
  10.             Account account = new Account();
  11.             account.Id = 101;
  12.             account.Name = "Neil Armstrong";
  13.             account.Balance = 100.00;
  14.             _accounts.Add(account.Id, account);
  15.         }
  16.       public Account AdjustBalance(Int32 id, Double adjustment)
  17.         {
  18.             Account account = null;
  19.             if (_accounts.ContainsKey(id))
  20.             {
  21.                 account = _accounts[id];
  22.                 account.Balance += adjustment;
  23.             }
  24.             return account;
  25.         }     
  26.     }
  27. }
复制代码
服务定义好了,我们下面就要在工作流中条用该服务,我们有三种方式:

代码方式

在工作流中定义三个属性:
  1. using System;
  2. using System.Workflow.Activities;
  3. namespace CaryWorkflows
  4. {
  5.     public sealed partial class BalanceAdjustmentWorkflow: SequentialWorkflowActivity
  6.     {
  7.         private Int32 _id;
  8.         private Double _adjustment;
  9.         private Account _account;
  10.         private IAccountServices _accountServices;

  11.         public Int32 Id
  12.         {
  13.             get { return _id; }
  14.             set { _id = value; }
  15.         }
  16.         public Double Adjustment
  17.         {
  18.             get { return _adjustment; }
  19.             set { _adjustment = value; }
  20.         }
  21.         public Account Account
  22.         {
  23.             get { return _account; }
  24.             set { _account = value; }
  25.         }
  26.         public BalanceAdjustmentWorkflow()
  27.         {
  28.             InitializeComponent();
  29.         }
  30.     }
  31. }
复制代码
然后我们向工作流中拖入一个CodeActivity,Activity有一个方法OnActivityExecutionContextLoad(),我们通过该的IServiceProvider的GetService方法来获取本地服务,代码如下:
  1. protected override void OnActivityExecutionContextLoad( IServiceProvider provider)
  2. {
  3.     base.OnActivityExecutionContextLoad(provider);         
  4.     _accountServices = provider.GetService(typeof(IAccountServices))as IAccountServices;
  5.     if (_accountServices == null)
  6.     {               
  7.         throw new InvalidOperationException("Unable to retrieve IAccountServices from runtime");
  8.     }
  9. }
复制代码
在CodeActivity的ExecuteCode事件中调用本地服务的方法,代码如下:
  1. private void codeAdjustAccount_ExecuteCode(object sender, EventArgs e)
  2. {
  3.     Account = _accountServices.AdjustBalance(Id, Adjustment);
  4. }
复制代码
最后要将该服务添加到工作流引擎当中去,
1. 先将ExternalDataExchangeService服务对象添加到引擎。2.再将我们自己开发的服务绑定到ExternalDataExchangeService服务中。
宿主程序的代码如下:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Workflow.Runtime;
  4. using System.Workflow.Activities;
  5. using CaryWorkflows ;
  6. namespace ConsoleLocalServices
  7. {
  8.     public class LocalServiceTest
  9.     {
  10.         public static void Run()
  11.         {
  12.             using (WorkflowRuntimeManager manager= new WorkflowRuntimeManager(new WorkflowRuntime()))
  13.             {
  14.                 AddServices(manager.WorkflowRuntime);
  15.                 manager.WorkflowRuntime.StartRuntime();               
  16.                 Dictionary<String, Object> wfArguments= new Dictionary<string, object>();             
  17.                 Console.WriteLine("开始....");
  18.                 wfArguments.Add("Id", 101);
  19.                 wfArguments.Add("Adjustment", -25.00);
  20.                 WorkflowInstanceWrapper instance = manager.StartWorkflow(                                  typeof(CaryWorkflows.BalanceAdjustmentWorkflow), wfArguments);
  21.                 manager.WaitAll(2000);
  22.                 Account account = instance.OutputParameters["Account"] as Account;
  23.                 if (account != null)
  24.                 {
  25.                     Console.WriteLine( "Revised Account: {0}, Name={1}, Bal={2:C}",account.Id,                                                                account.Name, account.Balance);
  26.                 }
  27.                 else
  28.                 {
  29.                     Console.WriteLine("Invalid Account Id\n\r");
  30.                 }

  31.                 Console.WriteLine("结束....");
  32.             }
  33.         }       
  34.         private static void AddServices(WorkflowRuntime instance)
  35.         {
  36.             ExternalDataExchangeService exchangeService = new ExternalDataExchangeService();
  37.             instance.AddService(exchangeService);           
  38.             exchangeService.AddService(new AccountService());
  39.         }
  40.     }
  41. }
复制代码
这样我们使用代码方式调用外部方法就结束了,结果如下:开始....Revised Account: 101, Name=Neil Armstrong, Bal=¥75.00结束....

配置文件方式

1.添加一个app.config到项目中,代码如下:
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <configSections>
  4.     <section name="WorkflowRuntime"
  5.       type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection,
  6.         System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral,
  7.         PublicKeyToken=31bf3856ad364e35" />
  8.     <section name="LocalServices"
  9.       type="System.Workflow.Activities.ExternalDataExchangeServiceSection,
  10.         System.Workflow.Activities, Version=3.0.0.0, Culture=neutral,
  11.         PublicKeyToken=31bf3856ad364e35"/>
  12.   </configSections>
  13.   <WorkflowRuntime Name="ConsoleLocalServices" >
  14.     <CommonParameters>
  15.       <!--Add parameters common to all services-->
  16.     </CommonParameters>
  17.     <Services>
  18.       <!--Add core services here-->
  19.     </Services>
  20.   </WorkflowRuntime>
  21.   <LocalServices >
  22.     <Services>
  23.       <!--Add local services here-->
  24.       <add type="CaryWorkflows.AccountService,
  25.         CaryWorkflows,Version=1.0.0.0,
  26.         Culture=neutral, PublicKeyToken=null" />
  27.     </Services>
  28.   </LocalServices >
  29. </configuration>
复制代码
2.我们只要需改动宿主程序中如下部分:
  1. using (WorkflowRuntimeManager manager= new WorkflowRuntimeManager(new WorkflowRuntime("WorkflowRuntime")));
  2. ExternalDataExchangeService exchangeService = new ExternalDataExchangeService("LocalServices");
复制代码
使用自定义活动方式

1.首先自定义一个活动(AdjustAccountActivity.cs), 我们在自定义活动中获取本地服务,并且调用其中方法,代码如下:
  1. using System;
  2. using System.ComponentModel;
  3. using System.Workflow.ComponentModel;
  4. using System.Workflow.Activities;
  5. namespace CaryWorkflows
  6. {
  7.     public partial class AdjustAccountActivity : Activity
  8.     {
  9.         public static DependencyProperty IdProperty= System.Workflow.ComponentModel              .DependencyProperty.Register("Id", typeof(Int32), typeof(AdjustAccountActivity));
  10.         [Description("Identifies the account")]
  11.         [Category("Local Services")]
  12.         [Browsable(true)]
  13.         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  14.         public Int32 Id
  15.         {
  16.             get
  17.             {
  18.                 return ((Int32)(base.GetValue(AdjustAccountActivity.IdProperty)));
  19.             }
  20.             set
  21.             {
  22.                 base.SetValue(AdjustAccountActivity.IdProperty, value);
  23.             }
  24.         }

  25.         public static DependencyProperty AdjustmentProperty = System.Workflow.ComponentModel.        DependencyProperty.Register("Adjustment", typeof(Double), typeof(AdjustAccountActivity));
  26.         [Description("The adjustment amount")]
  27.         [Category("Local Services")]
  28.         [Browsable(true)]
  29.         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  30.         public Double Adjustment
  31.         {
  32.             get
  33.             {
  34.                 return ((Double)(base.GetValue(AdjustAccountActivity.AdjustmentProperty)));
  35.             }
  36.             set
  37.             {
  38.                 base.SetValue(AdjustAccountActivity.AdjustmentProperty, value);
  39.             }
  40.         }

  41.         public static DependencyProperty AccountProperty= System.Workflow.ComponentModel.          DependencyProperty.Register("Account", typeof(Account), typeof(AdjustAccountActivity));
  42.         [Description("The revised Account object")]
  43.         [Category("Local Services")]
  44.         [Browsable(true)]
  45.         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  46.         public Account Account
  47.         {
  48.             get
  49.             {
  50.                 return ((Account)(base.GetValue( AdjustAccountActivity.AccountProperty)));
  51.             }
  52.             set
  53.             {
  54.                 base.SetValue(AdjustAccountActivity.AccountProperty, value);
  55.             }
  56.         }

  57.         public AdjustAccountActivity()
  58.         {
  59.             InitializeComponent();
  60.         }

  61.         protected override ActivityExecutionStatus Execute( ActivityExecutionContext                                                                                executionContext)
  62.         {
  63.             IAccountServices accountServices =executionContext.GetService<IAccountServices>();
  64.             if (accountServices == null)
  65.             {
  66.                 throw new InvalidOperationException( "fail IAccountServices from runtime");
  67.             }
  68.             Account = accountServices.AdjustBalance(Id, Adjustment);
  69.             return base.Execute(executionContext);
  70.         }
  71.     }
  72. }
复制代码
2.在工作流中我们将该自定义活动拖到工作流中,并设置相应的属性即可。

使用CallExternalMethodActivity

使用该方式我们只需要拖一个CallExternalMethodActivity到工作流中,并且设置起相应属性即可,如下图:
附件: 02_thumb.jpg

这三种方式的执行结果都是一样的。

上一篇:坚持学习WF(7):流程控制(Flow Control)
下一篇:坚持学习WF(9):本地服务之事件处理


文/生鱼片  出处/博客园
TOP