小测试,WCF分布式事务的用法以及回滚条件 .
小测试,WCF分布式事务的用法以及回滚条件
Host1 宿主A |
Host2 宿主B |
Host3 宿主C |
Client 客户端D |
数据层 |
业务层 |
表现层 |
测试用例:宿主A、B、C皆是WCF服务。宿主A中方法F1访问数据库Db1,宿主B中方法F2访问数据库Db2,宿主C中方法F3调用F1和F2(按先F1后F2的顺序调用),客户端D调用宿主C的方法F3,完成一个业务流程。
关于分布式事务的用法
在宿主A、B中,([TransactionFlow(TransactionFlowOption.Allowed)])指示F1、F2方法接受客户端传入事务,并允许加入到流事务中,业务层将不再需要以using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))的形式来显式开启事务。
上例中,如果业务层宿主C的F3方法单独调用F1方法,则F1也会升级成为事务,事务由F1开启,虽然仅一步数据库操作。如果宿主C的F3方法单独调用F2方法,则同理。如果宿主C的F3方法同时调用F1、F2(按先F1后F2的顺序调用),则F1、F2合为一个分布式事务,事务将由F3开启,F1、F2自动加入事务,并在F3方法结束后结束事务。F3中加入的宿主A、B的方法理论上无上限,皆可组合成为一个新的分布式事务。之所以可以这样用是因为宿主C也是一个WCF服务,并开启了事务范围。如果把宿主C换成客户端D的F4方法去同时调用F1、F2方法,则该F4方法并不会启用分布式事务(就跟用webservers一样了)。
关于F3分布式事务的回滚条件
令F1正常运行,F2出现异常,讨论F1的回滚条件。
宿主C方法F3:
宿主C方法F3 |
宿主A方法F1
|
宿主B方法F2
|
CatchB
|
CatchA |
宿主C方法F3 |
1、宿主C方法F3执行,F1执行成功,宿主B方法F2捕获到异常并且抛出,则宿主C方法F3的CatchA将捕获到异常,F3的CatchB没有捕获,客户端D也将捕获到异常:
此时F1方法的更新数据库操作被回滚,。
2、宿主C方法F3执行,F1执行成功,宿主B方法F2捕获到异常并且抛出,则宿主C方法F3的CatchA将捕获到异常(不抛出,情形同上),如果在宿主B方法F2的后面继续调用宿主B的其他方法,则F3的CatchB将捕获到异常:
,同时客户端D继续捕获到异常:
此时F1方法的更新数据库操作被回滚,。
3、宿主C方法F3执行,F1执行成功,宿主B方法F2捕获到异常并且抛出,则宿主C方法F3的CatchA将捕获到异常(情形同上),此时CatchA将捕获到的异常抛出: 则F3的CatchB也将捕获到异常(异常同上)。客户端D也将捕获到异常:
此时F1方法的更新数据库操作被回滚。
4、宿主C方法F3执行,F1执行成功,宿主B方法F2捕获到异常并且抛出,则宿主C方法F3的CatchA将捕获到异常(情形同上,CatchA不抛出),此时CatchB将捕获不到异常(抛不抛出就无所谓了)。客户端D仍将捕获到异常:
此时F1方法的更新数据库操作被回滚。(跟第一种情况是一样的,如果CatchA抛出,宿主B方法F2的后面继续调用宿主B的其他方法,则F3的CatchB将捕获到异常,此时跟第二种情况是一样的)。
5、宿主C方法F3执行,F1执行成功,宿主B方法F2捕获到异常不抛出,则宿主C方法F3的CatchA将捕获不到异常,F3的CatchB没有捕获,客户端D也将捕获不到异常:
此时F1方法的更新数据库操作不被回滚。
数据层方法抛出就肯定会回滚了,不抛业务层客户端都没捕获,就不回滚了。。
WCF中常见的异常类型?
WCF包括三种常见类型的异常:
1) 通讯异常,这通常是因为链路的原因,比如服务没有启动,网络阻塞等。这类异常是CommunicationException或者其派生类
2) 状态异常,这类异常通常是与上文提到的实例模式相关的,当访问了一个已经销毁的服务器对象时便会引发此类型的异常,它们通常是ObjectDisposedException
3) 服务异常,由服务端根据具体的业务逻辑触发,通常是FaultException 值得注意的是当抛出服务异常的时候,不同的实例模式的处理方式有所不同:
PerSession:这种模式下,抛出异常,服务实例将销毁,客户端抛出FaultException,客户端代理对象无法继续使用
PerCall:这种模式下,抛出异常,服务实例也将销毁。客户端代理对象无法继续使用
Single:这种模式下,抛出异常,服务实例会照旧运行。客户端代理无法继续使用。