WF提供了一组核心服务,例如在SQL 数据库中存储工作流实例的执行详细信息的持久性服务,计划服务,事务服务和跟踪服务。除了这些WF也提供了另外一种服务,叫做Local Service也可以叫做Data exchange service。主要是实现工作流和宿主程序之间的通信,使工作流能够使用方法和事件通过消息与外部系统交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序。 通过事件与工作流进行通信的功能提供了一种将数据发送到工作流的异步方式。本文主要讲述调用外部方法的部分。
下图说明本地通信服务如何与其主机应用程序通信:
下面首先说说如何开发一个本地服务:
1.使用C#的接口定义服务契约,在接口中定义你方法和事件。并使用[ExternalDataExchangeAttribute]装饰该接口,用于说明这是一个本地服务的接口。
2.开发一个实现了该接口的类,用于实现你的逻辑。
3.创建一个工作流实例,并将该本地服务添加到工作流引擎中去。
我们开发一个简单的本地服务的例子,根据AccountID来修改Balance的值,并使用三种方式来调用:
1.定义一个Account类,代码如下(Account.cs)
using System;
namespace CaryWorkflows
{
[Serializable]
public class Account
{
private Int32 _id;
private String _name = String.Empty;
private Double _balance;
public Int32 Id
{
get { return _id; }
set { _id = value; }
}
public String Name
{
get { return _name; }
set { _name = value; }
}
public Double Balance
{
get { return _balance; }
set { _balance = value; }
}
}
}
2.定义一个接口,需要ExternalDataExchange属性,代码如下(IAccountServices.cs):
using System; using System.Workflow.Activities; namespace CaryWorkflows { [ExternalDataExchange] public interface IAccountServices { Account AdjustBalance(Int32 id, Double adjustment); } }
3.实现该接口,代码如下():
using System; using System.Collections.Generic; namespace CaryWorkflows { public class AccountService : IAccountServices { private Dictionary<Int32, Account> _accounts= new Dictionary<int, Account>(); public AccountService() { Account account = new Account(); account.Id = 101; account.Name = "Neil Armstrong"; account.Balance = 100.00; _accounts.Add(account.Id, account); } public Account AdjustBalance(Int32 id, Double adjustment) { Account account = null; if (_accounts.ContainsKey(id)) { account = _accounts[id]; account.Balance += adjustment; } return account; } } }
服务定义好了,我们下面就要在工作流中条用该服务,我们有三种方式:
代码方式
在工作流中定义三个属性:
using System; using System.Workflow.Activities; namespace CaryWorkflows { public sealed partial class BalanceAdjustmentWorkflow: SequentialWorkflowActivity { private Int32 _id; private Double _adjustment; private Account _account; private IAccountServices _accountServices; public Int32 Id { get { return _id; } set { _id = value; } } public Double Adjustment { get { return _adjustment; } set { _adjustment = value; } } public Account Account { get { return _account; } set { _account = value; } } public BalanceAdjustmentWorkflow() { InitializeComponent(); } } }
然后我们向工作流中拖入一个CodeActivity,Activity有一个方法OnActivityExecutionContextLoad(),我们通过该
的IServiceProvider的GetService方法来获取本地服务,代码如下:
protected override void OnActivityExecutionContextLoad( IServiceProvider provider) { base.OnActivityExecutionContextLoad(provider); _accountServices = provider.GetService(typeof(IAccountServices))as IAccountServices; if (_accountServices == null) { throw new InvalidOperationException("Unable to retrieve IAccountServices from runtime"); } }
在CodeActivity的ExecuteCode事件中调用本地服务的方法,代码如下:
private void codeAdjustAccount_ExecuteCode(object sender, EventArgs e) { Account = _accountServices.AdjustBalance(Id, Adjustment); }
最后要将该服务添加到工作流引擎当中去,
1. 先将ExternalDataExchangeService服务对象添加到引擎。
2.再将我们自己开发的服务绑定到ExternalDataExchangeService服务中。
宿主程序的代码如下:
using System; using System.Collections.Generic; using System.Workflow.Runtime; using System.Workflow.Activities; using CaryWorkflows ; namespace ConsoleLocalServices { public class LocalServiceTest { public static void Run() { using (WorkflowRuntimeManager manager= new WorkflowRuntimeManager(new WorkflowRuntime())) { AddServices(manager.WorkflowRuntime); manager.WorkflowRuntime.StartRuntime(); Dictionary<String, Object> wfArguments= new Dictionary<string, object>(); Console.WriteLine("开始...."); wfArguments.Add("Id", 101); wfArguments.Add("Adjustment", -25.00); WorkflowInstanceWrapper instance = manager.StartWorkflow(
typeof(CaryWorkflows.BalanceAdjustmentWorkflow), wfArguments); manager.WaitAll(2000); Account account = instance.OutputParameters["Account"] as Account; if (account != null) { Console.WriteLine( "Revised Account: {0}, Name={1}, Bal={2:C}",account.Id,
account.Name, account.Balance); } else { Console.WriteLine("Invalid Account Id\n\r"); } Console.WriteLine("结束...."); } } private static void AddServices(WorkflowRuntime instance) { ExternalDataExchangeService exchangeService = new ExternalDataExchangeService(); instance.AddService(exchangeService); exchangeService.AddService(new AccountService()); } } }
这样我们使用代码方式调用外部方法就结束了,结果如下:
开始....
Revised Account: 101, Name=Neil Armstrong, Bal=¥75.00
结束....
配置文件方式
1.添加一个app.config到项目中,代码如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="WorkflowRuntime" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <section name="LocalServices" type="System.Workflow.Activities.ExternalDataExchangeServiceSection, System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </configSections> <WorkflowRuntime Name="ConsoleLocalServices" > <CommonParameters> <!--Add parameters common to all services--> </CommonParameters> <Services> <!--Add core services here--> </Services> </WorkflowRuntime> <LocalServices > <Services> <!--Add local services here--> <add type="CaryWorkflows.AccountService, CaryWorkflows,Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Services> </LocalServices > </configuration>2.我们只要需改动宿主程序中如下部分:
using (WorkflowRuntimeManager manager= new WorkflowRuntimeManager(new
WorkflowRuntime("WorkflowRuntime"))); ExternalDataExchangeService exchangeService = new ExternalDataExchangeService("LocalServices");
使用自定义活动方式
1.首先自定义一个活动(AdjustAccountActivity.cs), 我们在自定义活动中获取本地服务,并且调用其中方法,代码如下:
using System; using System.ComponentModel; using System.Workflow.ComponentModel; using System.Workflow.Activities; namespace CaryWorkflows { public partial class AdjustAccountActivity : Activity { public static DependencyProperty IdProperty= System.Workflow.ComponentModel
.DependencyProperty.Register("Id", typeof(Int32), typeof(AdjustAccountActivity)); [Description("Identifies the account")] [Category("Local Services")] [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Int32 Id { get { return ((Int32)(base.GetValue(AdjustAccountActivity.IdProperty))); } set { base.SetValue(AdjustAccountActivity.IdProperty, value); } } public static DependencyProperty AdjustmentProperty = System.Workflow.ComponentModel.
DependencyProperty.Register("Adjustment", typeof(Double), typeof(AdjustAccountActivity)); [Description("The adjustment amount")] [Category("Local Services")] [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Double Adjustment { get { return ((Double)(base.GetValue(AdjustAccountActivity.AdjustmentProperty))); } set { base.SetValue(AdjustAccountActivity.AdjustmentProperty, value); } } public static DependencyProperty AccountProperty= System.Workflow.ComponentModel.
DependencyProperty.Register("Account", typeof(Account), typeof(AdjustAccountActivity)); [Description("The revised Account object")] [Category("Local Services")] [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Account Account { get { return ((Account)(base.GetValue( AdjustAccountActivity.AccountProperty))); } set { base.SetValue(AdjustAccountActivity.AccountProperty, value); } } public AdjustAccountActivity() { InitializeComponent(); } protected override ActivityExecutionStatus Execute( ActivityExecutionContext
executionContext) { IAccountServices accountServices =executionContext.GetService<IAccountServices>(); if (accountServices == null) { throw new InvalidOperationException( "fail IAccountServices from runtime"); } Account = accountServices.AdjustBalance(Id, Adjustment); return base.Execute(executionContext); } } }
2.在工作流中我们将该自定义活动拖到工作流中,并设置相应的属性即可。
使用CallExternalMethodActivity
使用该方式我们只需要拖一个CallExternalMethodActivity到工作流中,并且设置起相应属性即可,如下图:![]()
这三种方式的执行结果都是一样的。
上一篇:坚持学习WF(7):流程控制(Flow Control)
下一篇:坚持学习WF(9):本地服务之事件处理
发表评论
-
平淡的2007
2007-12-24 08:04 824早上起来,送女朋友去公交车站,然后回来赶紧打开电脑,先 ... -
DreamSpark发布,高校学生免费使用Visual Studio 2008 Professional Edition 等微软软件
2008-02-20 13:23 1423今天上网无意中搜索到学生可以免费使用VS2008专业版,后来又 ... -
坚持学习WF(1):从HelloWorld开始
2008-04-04 16:30 902[置顶]坚持学习WF文章索 ... -
坚持学习WF(2):WF创作模式和设计时工具
2008-04-05 17:19 648[置顶]坚持学习WF文章索 ... -
坚持学习WF(3):WF框架概览
2008-04-08 07:27 789[置顶]坚持学习WF文章索 ... -
坚持学习WF(4):活动(Activity)和依赖属性(DependencyProperty)
2008-04-12 00:01 1157[置顶]坚持学习WF文章索引 活动(Activity) 活动 ... -
坚持学习WF(5):自定义活动(CustomActivity)
2008-04-13 15:25 946当WF提供的标准活动不能满足我们的需求的时候,我们就需要定义自 ... -
MOSS点滴(1):如何开发和部署feature
2008-04-16 21:35 839Features 是MOSS 2007以开箱即用的一套新功能, ... -
MOSS点滴(2):自定义Application Page
2008-04-19 20:07 852在MOSS中后台管理的页面都是Application Pag ... -
坚持学习WF(6):开发可复用的宿主程序
2008-04-21 21:45 699我们之前写工作流宿主 ... -
MOSS点滴(3):说说MOSS中的母版页
2008-04-25 21:15 1197MOSS中有两种页面:Site P ... -
MOSS点滴(4):实现Form认证
2008-04-29 21:12 716本文主要参考了网上的一些文章,但有些文章有些地方说的不是很明确 ... -
坚持学习WF(7):流程控制(Flow Control)
2008-04-30 18:10 854本文主要说说WF中和流 ... -
MOSS中的WebPart开发
2008-05-10 13:53 1074由于在asp.net1.1的时候asp.net中还没有webp ... -
坚持学习WF(9):本地服务之事件处理
2008-05-28 07:49 815[置顶]坚持学习WF文章索引 一:先来介绍两个活动 Even ... -
坚持学习WF(10):在工作流中使用关联
2008-06-01 13:03 706[置顶]坚持学习WF文章索 ... -
坚持学习WF(11):工作流通信与队列
2008-06-07 15:45 742[置顶]坚持学习WF文章索引 WF 提供的通信模型是构建于 ... -
MOSS中创建自定义内容类型
2008-06-12 20:23 1122一:简要介绍 某类内容 ... -
.NET中IDisposable接口的基本使用
2008-06-15 12:01 958首先来看MSDN中关于这个接口的说明: [ComVisible ... -
坚持学习WF(12):使用EventHandlingScopeActivity活动
2008-06-18 22:46 702[置顶]坚持学习WF文章索引 EventHandlingSco ...
相关推荐
坚持学习WF(1):从HelloWorld开始 本文主要通过实现了一个可以接受参数的HelloWorld程序来了解WF。 坚持学习WF(2):WF创作模式和设计时...坚持学习WF(8):本地服务之调用外部方法 坚持学习WF(9):本地服务之事件处理
坚持学习WF(8):本地服务之调用外部方法 坚持学习WF(9):本地服务之事件处理 工作流能够使用方法和事件通过消息与宿主程序交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序,8.9两篇...
坚持学习WF(8):本地服务之调用外部方法 坚持学习WF(9):本地服务之事件处理 工作流能够使用方法和事件通过消息与宿主程序交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序,8.9两篇...
python学习资源
jfinal-undertow 用于开发、部署由 jfinal 开发的 web 项目
基于Andorid的音乐播放器项目设计(国外开源)实现源码,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。
python学习资源
python学习资源
python学习一些项目和资源
【毕业设计】java-springboot+vue家具销售平台实现源码(完整前后端+mysql+说明文档+LunW).zip
HTML+CSS+JavaScarip开发的前端网页源代码
python学习资源
【毕业设计】java-springboot-vue健身房信息管理系统源码(完整前后端+mysql+说明文档+LunW).zip
成绩管理系统C/Go。大学生期末小作业,指针实现,C语言版本(ANSI C)和Go语言版本
1_基于大数据的智能菜品个性化推荐与点餐系统的设计与实现.docx
【毕业设计】java-springboot-vue交流互动平台实现源码(完整前后端+mysql+说明文档+LunW).zip
内容概要:本文主要探讨了在高并发情况下如何设计并优化火车票秒杀系统,确保系统的高性能与稳定性。通过对比分析三种库存管理模式(下单减库存、支付减库存、预扣库存),强调了预扣库存结合本地缓存及远程Redis统一库存的优势,同时介绍了如何利用Nginx的加权轮询策略、MQ消息队列异步处理等方式降低系统压力,保障交易完整性和数据一致性,防止超卖现象。 适用人群:具有一定互联网应用开发经验的研发人员和技术管理人员。 使用场景及目标:适用于电商、票务等行业需要处理大量瞬时并发请求的业务场景。其目标在于通过合理的架构规划,实现在高峰期保持平台的稳定运行,保证用户体验的同时最大化销售额。 其他说明:文中提及的技术细节如Epoll I/O多路复用模型以及分布式系统中的容错措施等内容,对于深入理解大规模并发系统的构建有着重要指导意义。
基于 OpenCV 和 PyTorch 的深度车牌识别
【毕业设计-java】springboot-vue教学资料管理系统实现源码(完整前后端+mysql+说明文档+LunW).zip
此数据集包含有关出租车行程的详细信息,包括乘客人数、行程距离、付款类型、车费金额和行程时长。它可用于各种数据分析和机器学习应用程序,例如票价预测和乘车模式分析。