WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx

上传人:b****3 文档编号:27478819 上传时间:2023-07-02 格式:DOCX 页数:15 大小:20.96KB
下载 相关 举报
WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx_第1页
第1页 / 共15页
WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx_第2页
第2页 / 共15页
WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx_第3页
第3页 / 共15页
WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx_第4页
第4页 / 共15页
WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx

《WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx》由会员分享,可在线阅读,更多相关《WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx(15页珍藏版)》请在冰豆网上搜索。

WCF技术剖析之一一步步创建一个完整的分布式事务应用.docx

WCF技术剖析之一一步步创建一个完整的分布式事务应用

WCF技术剖析之一:

一步步创建一个完整的分布式事务应用

在完成了对于WCF事务编程(《上篇》、《中篇》、《下篇》)的介绍后,本篇文章将提供一个完整的分布式事务的WCF服务应用,通过本例,读者不仅仅会了解到如何编程实现事务型服务,还会获得其他相关的知识,比如DTC和AS-AT的配置等。

本例还是沿用贯通本章的应用场景:

银行转帐。

我们将会创建一个BankingService服务,并将其中的转帐操作定义成事务型操作。

我们先从物理部署的角度来了解一下BankingService服务,以及需要实现怎样的分布式事务。

一、从部署的角度看分布式事务

既然是实现分布式事务,那么事务会跨越多台机器。

简单起见,我使用两台机器来模拟。

有条件的读者可以在自己的局域网中进行练习,如果你没有局域网可用,你可以使用虚拟机来模拟局域网。

假设两台机器名分别是Foo和Bar,整个应用的物理拓扑结构如图1所示。

image

图1BankingService物理部署拓扑

BankingService和客户端部署与主机Foo,定义在BankingService的转账的两个子操作“提取(Withdraw)”和“存储(Deposit)”通过调用部署于主机Bar的同名服务(WithdrawService和DepositService)实现。

而WithdrawService和DepositService最终实现对存储于数据库(这里是SQLServer)的数据进行修改,MSSQL部署与主机Bar中。

实际上,整个应用主要涉及到对三个服务(BankingService、WithdrawService和DepositService)的实现,我们先来看看服务契约和服务的实现。

步骤1:

服务契约和服务的实现

我们仍然采用契约共享的方式将服务契约定义在单独的项目之中,共服务端和客户端共享。

涉及到的三个服务对应的服务契约定义如下,事务型操作的TransactionFlow选项被设置为Allwed(默认值)。

IBankingService:

1:

usingSystem.ServiceModel;

2:

namespaceArtech.TransactionalService.Service.Interface

3:

{

4:

[ServiceContract(Namespace="

5:

publicinterfaceIBankingService

6:

{

7:

[OperationContract]

8:

[TransactionFlow(TransactionFlowOption.Allowed)]

9:

voidTransfer(stringfromAccountId,stringtoAccountId,doubleamount);

10:

}

11:

}

IWithdrawService:

1:

usingSystem.ServiceModel;

2:

namespaceArtech.TransactionalService.Service.Interface

3:

{

4:

[ServiceContract(Namespace="

5:

publicinterfaceIWithdrawService

6:

{

7:

[OperationContract]

8:

[TransactionFlow(TransactionFlowOption.Allowed)]

9:

voidWithdraw(stringaccountId,doubleamount);

10:

}

11:

}

IDepositService:

1:

usingSystem.ServiceModel;

2:

namespaceArtech.TransactionalService.Service.Interface

3:

{

4:

[ServiceContract(Namespace="

5:

publicinterfaceIDepositService

6:

{

7:

[OperationContract]

8:

[TransactionFlow(TransactionFlowOption.Allowed)]

9:

voidDeposit(stringaccountId,doubleamount);

10:

}

11:

}

实现了服务操作的IWithdrawService和IDepositService的WithdrawService和DepositService分别实现基于给定银行账户的提取和存储操作。

限于篇幅的问题,具体对数据库相应数据的更新操作就不再这里一一介绍了。

下面是WithdrawService和DepositService的定义,由于不管是单独被调用,还是作为转帐的一个子操作,Withdraw和Deposit操作均需要在一个事务中执行,所以我们需要通过应用OperationBehaviorAttribute将TransactionScopeRequired属性设为True。

WithdrawService:

1:

usingSystem.ServiceModel;

2:

usingArtech.TransactionalService.Service.Interface;

3:

namespaceArtech.TransactionalService.Service

4:

{

5:

publicclassWithdrawService:

IWithdrawService

6:

{

7:

[OperationBehavior(TransactionScopeRequired=true)]

8:

publicvoidWithdraw(stringaccountId,doubleamount)

9:

{

10:

//省略实现

11:

}

12:

}

13:

}

DepositService:

1:

usingSystem.ServiceModel;

2:

usingArtech.TransactionalService.Service.Interface;

3:

namespaceArtech.TransactionalService.Service

4:

{

5:

publicclassDepositService:

IDepositService

6:

{

7:

[OperationBehavior(TransactionScopeRequired=true)]

8:

publicvoidDeposit(stringaccountId,doubleamount)

9:

{

10:

//省略实现

11:

}

12:

}

13:

}

定义在BankingService的Transfer操作就是调用上述的两个服务,由于服务调用设置到对服务代理的关闭以及异常的处理(相关的内容在《WCF技术剖析(卷1)》的第8章有详细的介绍),为了实现代码的复用,我定义了一个静态的ServiceInvoker类。

ServiceInvoker定义如下,泛型方法Invoke用于进行服务的调用,并实现了服务代理的关闭(Close),以及异常抛出是对服务代理的中止(Abort)。

Invoke的泛型参数类型为服务契约类型,方法接受两个操作,委托action代表服务调用操作,endpointConfigurationName表示配置的终结点名称。

1:

usingSystem;

2:

usingSystem.ServiceModel;

3:

namespaceArtech.TransactionalService.Service.Interface

4:

{

5:

publicstaticclassServiceInvoker

6:

{

7:

publicstaticvoidInvoke(Actionaction,stringendpointConfigurationName)

8:

{

9:

Guard.ArgumentNotNull(action,"action");

10:

Guard.ArgumentNotNullOrEmpty(endpointConfigurationName,"endpointConfigurationName");

11:

12:

using(ChannelFactorychannelFactory=newChannelFactory(endpointConfigurationName))

13:

{

14:

TChannelchannel=channelFactory.CreateChannel();

15:

using(channelasIDisposable)

16:

{

17:

try

18:

{

19:

action(channel);

20:

}

21:

catch(TimeoutException)

22:

{

23:

(channelasICommunicationObject).Abort();

24:

throw;

25:

}

26:

catch(CommunicationException)

27:

{

28:

(channelasICommunicationObject).Abort();

29:

throw;

30:

}

31:

}

32:

}

33:

}

34:

}

35:

}

那么,借助于ServiceInvoker,BankingService的定义就很简单了。

对于Transfer操作,我们依然通过OperationBehaviorAttribute特性将TransactionScopeRequired设置成True。

1:

usingSystem;

2:

usingSystem.Collections.Generic;

3:

usingSystem.ServiceModel;

4:

usingArtech.TransactionalService.Service.Interface;

5:

namespaceArtech.TransactionalService.Service

6:

{

7:

publicclassBankingService:

IBankingService

8:

{

9:

[OperationBehavior(TransactionScopeRequired=true)]

10:

publicvoidTransfer(stringfromAccountId,stringtoAccountId,doubleamount)

11:

{

12:

ServiceInvoker.Invoke(proxy=>proxy.Withdraw(fromAccountId,amount),"withdrawservice");

13:

ServiceInvoker.Invoke(proxy=>proxy.Deposit(toAccountId,amount),"depositservice");

14:

}

15:

}

16:

}

步骤2:

部署服务

BankingService和依赖的WithdrawService与DepositService已经定义好了,现在我们需要对它们进行部署。

本实例采用基于IIS的服务寄宿方式,在进行部署之前需要为三个服务创建.svc文件。

在这里,我.svc文件命名为与服务类型相同的名称(BankingService.svc、WithdrawService.svc和DepositService.svc)。

关于.svc文件的具体定义,在这里就不再重复介绍了,对此不了解的读者,可以参阅《WCF技术剖析(卷1)》第7章关于IIS服务寄宿部分。

我们需要分别在主机Foo和Bar上创建两个IIS虚拟目录(假设名称为Banking),并将定义服务契约和服务类型的两个程序集拷贝到Foo\Banking\Bin和Bar\Banking\Bin。

然后再将BankingService.svc拷贝到Foo\Banking下,将WithdrawService.svc和DepositService.svc拷贝到Bar\Banking下。

最后,我们需要创建两个Web.config,分别拷贝到Foo\Banking\Bin和Bar\Banking下面。

下面两段XML代表两个Web.config的配置。

Foo\Banking\Web.config:

1:

xmlversion="1.0"encoding="utf-8"?

>

2:

3:

4:

5:

6:

7:

8:

9:

10:

11:

12:

13:

14:

15:

16:

17:

18:

19:

20:

21:

22:

Bar\Banking\Web.config:

1:

xmlversion="1.0"encoding="utf-8"?

>

2:

3:

4:

5:

6:

7:

8:

9:

10:

11:

12:

13:

14:

15:

16:

17:

18:

19:

//Bar/banking/withdrawservice.svc"binding="customBinding"bindingConfiguration="transactionalBinding"

20:

contract="Artech.TransactionalService.Service.Interface.IWithdrawService"/>

21:

//Bar/banking/depositservice.svc"binding="customBinding"bindingConfiguration="transactionalBinding"

22:

contract="Artech.TransactionalService.Service.Interface.IDepositService"/>

23:

24:

25:

步骤3:

调用BankingService

现在我们已经部署好了定义的三个服务,现在我们可以调用它们实施转帐处理了。

我们可以像调用普通服务一样调用BankingService,无须考虑事务的问题。

因为我们通过OperationBehaviorAttribute特性将BankingService的Transfer操作的TransactionScopeRequired设置成True,这会确保整个操作的执行是在一个事务中进行(可能是流入的事务,也可能是重新创建的事务)。

下面进行转帐处理的客户端代码和配置。

1:

stringfromAccountId="123456789";

2:

stringtooAccountId="987654321";

3:

doubleamount=1000;

4:

ServiceInvoker.Invoke(proxy=>proxy.Transfer(fromAccountId,tooAccountId,amount),"bankingservice");

配置:

1:

xmlversion="1.0"encoding="utf-8"?

>

2:

3:

4:

5:

6:

7:

8:

9:

10:

11:

12:

13:

14:

//Foo/banking/bankingservice.svc"

15:

binding="customBinding"bindingConfiguration="transactionalBinding"contract="Artech.TransactionalService.Service.Interface.IBankingService"

16:

name="bankingservice"/>

17:

18:

19:

实际上,由于定义在BankingService的Transfer操作完全是通过调用WithdrawService和DepositService实现的,我们也可以绕过BankingService直接调用这两个服务实现转账的处理。

为此我们需要在配置中添加调用WithdrawService和DepositService的终结点:

1:

xmlversion="1.0"encoding="utf-8"?

>

2:

3:

4:

......

5:

6:

//Bar/banking/withdrawservice.svc"binding="customBinding"bindingConfiguration="transactionalBinding"contract="Artech.TransactionalService.Service.Interface.IWithdrawService"/>

7:

//Bar/banking/depositservice.svc"binding="customBinding"bindingCfiguration="transactionalBinding"contract="Artech.TransactionalService.Service.Interface.IDepositService"/>

8:

9:

10:

由于整个转帐的操作必须纳入到一个事务中进行,并且客户端主动发起对WithdrawService和DepositService两个服务的调用,所以客户端是事物的初始化者。

为此,我们需要将对这两个服务的调用放到一个TransactionScope中进行,相应的代码如下所示:

1:

stringfromAccountId="123456789";

2:

stringtooAccountId="987654321";

3:

doubleamount=1000;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1