WCF 事务的运用
本文主要内容:1.WCF中运用事务的基本设置;2.代码示例;
一、WCF运用事务的基本设置包括三项:
a.绑定中添加事务流 transactionFlow=true;
b.操作契约中添加[TransactionFlow(TransactionFlowOption....)]属性;
c.服务类中添加事务环境[OperationBehavior(TransactionAutoComlete=true,TransactionScopeRequired=true)];
d.如果服务类的实例不是 InstanceContextMode.PerCall,则需要在[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete=false)];
下面是它们的组合,会产生不同的事务传播模式
绑定事务流 |
TransactionFlowOption |
TransactionScopeRequired |
事务模式 |
False |
Allowed |
False |
None |
False |
Allowed |
True |
Service |
False |
NotAllowed |
False |
None |
False |
NotAllowed |
True |
Service |
True |
Allowed |
False |
None |
True |
Allowed |
True |
Client/Service |
True |
Mandatory |
False |
None |
True |
Mandatory |
True |
client |
Client/Service模式:客户端包含事务时,以客户端的事务为根;如果客户端没有包含事务,则以服务端为事务的根;
Client模式:始终以客户端的事务为根;
Service模式:以服务端的事务为根;
None模式:没有运用到事务;
二、代码示例:向数据库插入数据
服务端:
操作契约 ITransaction.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6 namespace TransactionExampleHost
7 {
8 [ServiceContract]
9 public interface ITransaction
10 {
11 [OperationContract]
12 [TransactionFlow(TransactionFlowOption.Allowed)]
13 void ExecuteSql(string sql);
14
15 [OperationContract]
16 void MethodException();
17 }
18 }
服务类 TransactionService.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 using System.ServiceModel;
7 namespace TransactionExampleHost
8 {
9 [ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete=false,InstanceContextMode=InstanceContextMode.PerCall)]
10 class TransactionService:ITransaction
11 {
12 public TransactionService()
13 {
14 Console.WriteLine("Constructor....");
15 }
16
17 [OperationBehavior(TransactionAutoComplete=true,TransactionScopeRequired=true)]
18 public void ExecuteSql(string sql)
19 {
20 DbHelper dbHelper = new DbHelper("Data Source=.;Initial Catalog=db_Manager;Integrated Security=True");
21 dbHelper.ExecuteSql(sql);
22 }
23
24 public void MethodException()
25 {
26 throw new Exception("exception is open");
27 }
28 }
29 }
数据库操作类 DbHelper.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data;
6 using System.Data.SqlClient;
7 namespace TransactionExampleHost
8 {
9 class DbHelper
10 {
11 private string conn;
12 public DbHelper(string strCon)
13 {
14 this.conn = strCon;
15 }
16
17 private SqlConnection Open()
18 {
19 SqlConnection sqlCon = new SqlConnection(conn);
20 if (sqlCon.State != ConnectionState.Open)
21 {
22 sqlCon.Open();
23 }
24 return sqlCon;
25 }
26
27 private void Close(SqlConnection sqlCon)
28 {
29 if (sqlCon.State != ConnectionState.Closed)
30 {
31 sqlCon.Close();
32 }
33 }
34
35 public void ExecuteSql(string sql)
36 {
37 using (SqlCommand cmd = new SqlCommand())
38 {
39 cmd.CommandText = sql;
40 cmd.Connection = this.Open();
41 cmd.ExecuteNonQuery();
42 this.Close(cmd.Connection);
43 }
44 }
45 }
46 }
Program.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6 namespace TransactionExampleHost
7 {
8 class Program
9 {
10 static void Main(string[] args)
11 {
12 ServiceHost host = new ServiceHost(typeof(TransactionService));
13 host.Opened += (r1, r2) => Console.WriteLine("Host is open......\r\nPress any key to exit.....");
14 host.Open();
15 Console.ReadKey();
16 }
17 }
18 }
App.config
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <system.serviceModel>
4 <services>
5 <service name="TransactionExampleHost.TransactionService">
6 <endpoint address="net.tcp://localhost:6666" binding="netTcpBinding" contract="TransactionExampleHost.ITransaction"
7 bindingConfiguration="transactionBinding"></endpoint>
8 </service>
9 </services>
10 <bindings>
11 <netTcpBinding>
12 <binding name="transactionBinding" transactionFlow="true">
13 <reliableSession enabled="true"/>
14 </binding>
15 </netTcpBinding>
16 </bindings>
17 </system.serviceModel>
18 </configuration>
客户端:
操作契约 ITransaction.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6 namespace TransactionExampleClient
7 {
8 [ServiceContract]
9 public interface ITransaction
10 {
11 [OperationContract]
12 [TransactionFlow(TransactionFlowOption.Allowed)]
13 void ExecuteSql(string sql);
14
15 [OperationContract]
16 void MethodException();
17 }
18 }
代理类 TransactionProxy.cs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.ServiceModel;
6 using System.ServiceModel.Transactions;
7 using System.ServiceModel.Channels;
8 namespace TransactionExampleClient
9 {
10 class TransactionProxy:ClientBase<ITransaction>,ITransaction
11 {
12 public TransactionProxy()
13 : base()
14 { }
15
16 public TransactionProxy(string configurationName):base(configurationName)
17 {}
18
19 public void ExecuteSql(string sql)
20 {
21 base.Channel.ExecuteSql(sql);
22 }
23
24 public void MethodException()
25 {
26 base.Channel.MethodException();
27 }
28 }
29 }
创建窗体,并且添加一按钮,执行以下事件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void btnExecuteProxy_Click(object sender, EventArgs e)
2 {
3 try
4 {
5 using (TransactionScope scope = new TransactionScope())
6 {
7 TransactionProxy proxy = new TransactionProxy("dbTransaction");
8 string sql = "insert into userinfo(UserId,name,age) values(24,'aa',1)";
9 proxy.ExecuteSql(sql);
10
11 TransactionProxy proxy2 = new TransactionProxy("dbTransaction");
12 string sql2 = "insert into userinfo(UserId,name,age) values(25,'aa',1)"; //如果需要测试回滚,可以将SQL语句写错
13 proxy2.ExecuteSql(sql2);
14
15 scope.Complete(); //此处提交事务。如果没有执行此方法,事务就会回滚。
16 MessageBox.Show("Execute finish!");
17
18 }
19 }
20 catch (Exception ex)
21 {
22 MessageBox.Show("Test", ex.Message);
23 }
24 }
app.config:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <system.serviceModel>
4 <client>
5 <endpoint name="dbTransaction" address="net.tcp://localhost:6666" binding="netTcpBinding" contract="TransactionExampleClient.ITransaction"
6 bindingConfiguration="transactionBinding"></endpoint>
7 </client>
8 <bindings>
9 <netTcpBinding>
10 <binding name="transactionBinding" transactionFlow="true" >
11 <reliableSession enabled="true"/>
12 <security></security>
13 </binding>
14 </netTcpBinding>
15 </bindings>
16 </system.serviceModel>
17 </configuration>
另外需要在数据库创建一测试表userinfo:包含(UserId,name,age)这三字段。
参考文献:《WCF 服务编程》