最近在学WCF,所以有两个设想疑问(菜鸟多疑问):
- 如果有WCF服务A,WCF服务B,客户端调用WCF服务A插入一条数据,然后再调用服务B也插入一条数据,然而服务B出错了进行了回滚,服务A能不能也进行回滚操作?
- 或是客户端调用服务A和服务B,成功了插入了两条数据之后,客户端这边出错了,如何让服务A和服务B插入的数据也回滚操作?
经过学习探索,了解到WCF支持分布式事务,事务可以在多个服务中传播,也可以在服务端与客户端之间传播。
注:WCF内并非所有绑定都支持事务的,常用的BasicHttpBinding就不支持事务的传播。只有以下几个绑定才能支持事务流的运转:NetTcpBinding、WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding、NetNamedPipeBinding。
代码尝试
先建一个WCF服务(WSHttpBinding),有两个Add方法,在客户端两次分开调用
[ServiceContract] public interface IService1 { [OperationContract] string AddData_1(string a); [OperationContract] string AddData_2(string a); }
public class Service1 : IService1 { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] [TransactionFlow(TransactionFlowOption.Allowed)] public string AddData_1(string b) { using (testEntities ts = new testEntities()) { Student a = new Student() { Age = 9999999, Class = b, Sex = "男", Score = 100 }; ts.Student.Add(a); ts.SaveChanges(); } return "22222"; } [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] [TransactionFlow(TransactionFlowOption.Allowed)] public string AddData_2(string b) { using (testEntities ts = new testEntities()) { Student a = new Student() { Age = 9999999, Class = b, Sex = "男", Score = 100 }; ts.Student.Add(a); ts.SaveChanges(); throw new Exception("出错拉!"); //抛出错误 } return "22222"; } }
其中标红解释:
TransactionScopeRequired:值为true时,表示此WCF服务的方法中启动事务,为false,反之不启动。
TransactionAutoComplete:值为true时为隐式事务,方法在运行过程中没有抛出Exception,事务会自动提交。如果期间出现任何异常,事务就会自动回滚;false时,显式事务,必须OperationContext.Current.SetTransactionComplete () 显式提交事务。
TransactionFlowOption:设置为Allowed,开启事务传播,客户端的事务传播到服务端。
有三个值:
NotAllowed,禁止客户端传播事务流到服务端,即使客户端启动了事务,该事务也会被忽略;
Allowed,允许客户端的事务传播到服务端,但服务器端不一定会引用到此事务(如果服务端方法没开启事务);
Mandatory,服务端与客户端必须同时启动事务流,否则就会抛出InvalidOperationException异常。
web.config配置(重要)
服务端web.config
<configuration> <system.serviceModel> <bindings> <wsHttpBinding> <!--启动事务流--> <binding name="defaultWSHttpBinding" transactionFlow="true" /> <!--这里一定要设置绑定的transactionFlow为true,否则无法使用事务--> </wsHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior> <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false --> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 --> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="WcfDemo_shiwu.Service1"> <endpoint address="" binding="wsHttpBinding" contract="WcfDemo_shiwu.IService1" bindingConfiguration="defaultWSHttpBinding"/> <!--这里一定要设置bindingConfiguration--> </service> </services> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true" /> <!-- 若要在调试过程中浏览 Web 应用程序根目录,请将下面的值设置为 True。 在部署之前将该值设置为 False 可避免泄露 Web 应用程序文件夹信息。 --> <directoryBrowse enabled="true" /> </system.webServer> </configuration>
客户端
<configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IService1" transactionFlow="true" /> </wsHttpBinding> </bindings> <client> <endpoint address="http://localhost:5854/Service1.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="TestService.IService1" name="WSHttpBinding_IService1"> <identity> <userPrincipalName value="DESKTOP-350R9O7qiuguochao" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>
客户端代码
情况一:WCF服务中的方法2报错回滚,那么之前第一次调用插入的是否成功回滚?
static void Main(string[] args) { //客户端开启分布式事务,要引用System.Transactions.dll using (TransactionScope ts = new TransactionScope()) { //调用服务 using (TestService.Service1Client service = new TestService.Service1Client()) { service.AddData_1("1"); }
Console.WriteLine("第一次调用:成功插入一条数据");
//重新去调用WCF服务 using (TestService.Service1Client service2 = new TestService.Service1Client()) { //WCF中的AddData_2会报错 service2.AddData_2("2"); } ts.Complete(); } }
结果:
WCF服务插入第二条数据,已经报错,因为该方法中已经设置 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)],开启了事务,所以第二数据肯定是会被回滚的
且看第一条数据是否也会被回滚?,结果是两条数据都被回滚了
情况2:两次调用WCF服务成功插入数据后,客户端报错回滚,在WCF服务中插入的数据是否也回滚?
将抛出错误注释掉,重新运行WCF服务
改写客户端代码,并设下断点:
执行到断点,查看数据情况
已经成功插入,继续执行,由于没有进行提交,所以该事务将会自动回滚,且看在WCF服务中插入的数据是否会被回滚
客户端进行了回滚操作,很显然,客户端的事务传播的WCF中的方法事务,也将相应的操作回滚。