在本人的 “ .NET简谈事务本质论”一文中我们从整体上了解了事务模型,在我们脑子里能有一个全局的事务处理结构,消除对数据库事务的依赖理解,重新认识事务编程模型。
insert into test values('222') --我们在表test中插入一条记录 go begin transaction tr begin try begin insert into test2 values('111') insert into test values('222') --该行插入会导致主键冲突,也就是我们要的效果 end commit transaction tr end try begin catch print '事务执行错误!' print error_number() rollback transaction tr end catch
例2:
public class Test { SqlConnection conn = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); public void Add() { conn.Open(); SqlCommand command = new SqlCommand("insert into test2 values(111)", conn); try { command.Transaction = conn.BeginTransaction(); command.ExecuteNonQuery(); command.CommandText = "insert into test values(222)"; command.ExecuteNonQuery(); command.Transaction.Commit(); } catch (Exception err) { Console.WriteLine(err); command.Transaction.Rollback(); } } }
这就是典型ADO.NET事务处理代码,其实和我们第一个例子中的T-SQL代码是差不多的,通过ADO.NET中的SqlConnection.BeginTransaction()获取到对底层ODBC中的数据库事务的引用,其实这里还没有真正的设计到.NET中的事务处理代码,这里只是对数据库管理系统的远程调用,通过远程处理的消息通讯进行事务处理远程化。
public class DisplayTransactioninfo { public static void Display(System.Transactions.Transaction tr) { if (tr != null) { Console.WriteLine("Createtime:" + tr.TransactionInformation.CreationTime); Console.WriteLine("Status:" + tr.TransactionInformation.Status); Console.WriteLine("Local ID:" + tr.TransactionInformation.LocalIdentifier); Console.WriteLine("Distributed ID:" + tr.TransactionInformation.DistributedIdentifier); Console.WriteLine(); } }
CommittableTransaction事务处理
public class Test3 { SqlConnection conn; CommittableTransaction committran = new CommittableTransaction(); public Test3() { conn = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); DisplayTransactioninfo.Display(committran); } public void Add3() { conn.Open(); conn.EnlistTransaction(committran);//需要将本次的连接操作视为事务性的 SqlCommand command = new SqlCommand(); try { command.Connection = conn; command.CommandText = "insert into test2 values(111)"; command.ExecuteNonQuery(); command.CommandText = "insert into test values(222)"; command.ExecuteNonQuery(); committran.Commit(); } catch (Exception err) { committran.Rollback(); //出现出错执行回滚操作} } }
[System.Runtime.InteropServices.ComVisible(true)] //COM+是在COM的基础上发展起来的,需要将.NET程序集中的类型公开为COM组件。 [System.EnterpriseServices.Transaction(TransactionOption.Required)]//始终需要事务处理域 public class Test2 : ServicedComponent { public Test2() { } SqlConnection conn = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); [AutoComplete(true)] //如果在方法的执行体类没有出现错误,那么将自动设置事务处理的结果 public void Add2() { conn.Open(); SqlCommand command = new SqlCommand(); try { command.Connection = conn; command.CommandText = "insert into test2 values(111)"; command.ExecuteNonQuery(); command.CommandText = "insert into test values(222)"; command.ExecuteNonQuery(); } catch { System.EnterpriseServices.ContextUtil.SetAbort(); } } }
DependentTransaction跨线程事务处理
我们在编写高并发量程序时,都会用到多线程来进行处理,让主线程能有时间来处理第一线的请求,然后将请求分发到各个子线程上进行后台的处理。我们来看一幅图:
public class Test6 { CommittableTransaction commit = new CommittableTransaction(); SqlConnection conn1 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); public Test6() { conn1.Open(); conn1.EnlistTransaction(commit); } public void Add6() { try { DisplayTransactioninfo.Display(commit); SqlCommand command = new SqlCommand("insert into test2 values(111)", conn1); command.ExecuteNonQuery(); Thread thread = new Thread(Test6.CommitThread); thread.Start(commit.DependentClone(DependentCloneOption.BlockCommitUntilComplete)); commit.Commit(); } catch (Exception err) { commit.Rollback(); } } public static void CommitThread(object co) { DependentTransaction commit = co as DependentTransaction; SqlConnection conn2 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); conn2.Open(); conn2.EnlistTransaction(commit as DependentTransaction); DisplayTransactioninfo.Display(commit); SqlCommand command = new SqlCommand("insert into test values(111)", conn2); try { command.ExecuteNonQuery(); commit.Complete(); } catch (Exception err) { Console.WriteLine(err); commit.Rollback(); } } }
public class Test4 { SqlConnection conn1 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); SqlConnection conn2 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); CommittableTransaction committran = new CommittableTransaction(); public Test4() { DisplayTransactioninfo.Display(committran); conn1.Open(); conn1.EnlistTransaction(committran); conn2.Open(); conn2.EnlistTransaction(committran); DisplayTransactioninfo.Display(committran); } public void Add4() { try { SqlCommand command1 = new SqlCommand("insert into test2 values(111)", conn1); command1.ExecuteNonQuery(); SqlCommand command2 = new SqlCommand("insert into test values(222)", conn2); command2.ExecuteNonQuery(); } catch (Exception err) { Console.WriteLine(err); committran.Rollback(); } } }
//服务契约(ServiceContract): [ServiceContract(SessionMode = SessionMode.Required)] public interface IDistributedTransaction { [TransactionFlow(TransactionFlowOption.Allowed)] [OperationContract] void Add(); }
//服务类1: public class DistributedTransactionService1 : IDistributedTransaction { SqlConnection conn1 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); #region IDistributedTransaction [OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)] public void Add() { conn1.Open(); SqlCommand command = new SqlCommand("insert into test2 values(111)", conn1); command.ExecuteNonQuery(); DataBaseOperation.DisplayTransactioninfo.Display(System.Transactions.Transaction.Current); } #endregion }
//服务类2: public class DistributedTransactionService2 : IDistributedTransaction { SqlConnection conn2 = new SqlConnection("data source=.;Initial Catalog=DataMedicine;Integrated Security=SSPI"); #region IDistributedTransaction [OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)] public void Add() { conn2.Open(); SqlCommand command = new SqlCommand("insert into test values(222)", conn2); try { DataBaseOperation.DisplayTransactioninfo.Display(System.Transactions.Transaction.Current); command.ExecuteNonQuery(); } catch (Exception err) { throw err; } }
<service name="ServerConsole.Transaction.DistributedTransactionService1" behaviorConfiguration="metadatabehaviors"> <host> <baseAddresses> <add baseAddress="http://localhost:8027"/> <add baseAddress="net.tcp://localhost:8028"/> </baseAddresses> </host> <endpoint address="DistributedTransactionService1" binding="netTcpBinding" bindingConfiguration="tranbinding" contract="ServerConsole.Transaction.IDistributedTransaction"></endpoint> </service> <service name="ServerConsole.Transaction.DistributedTransactionService2" behaviorConfiguration="metadatabehaviors"> <host> <baseAddresses> <add baseAddress="http://localhost:8029"/> <add baseAddress="net.tcp://localhost:8030"/> </baseAddresses> </host> <endpoint address="DistributedTransactionService2" binding="netTcpBinding" bindingConfiguration="tranbinding" contract="ServerConsole.Transaction.IDistributedTransaction"></endpoint> </service>
Binding配置:
<bindings> <netTcpBinding> <binding name="tranbinding" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004"> <reliableSession enabled="true" ordered="true"/> </binding> </netTcpBinding> </bindings>
我们需要打开Binding的事务流传递。
客户端代码:
DistributedTransactionClient.DistributedTransactionClient tranclient = new DistributedTransactionClient.DistributedTransactionClient(); DistributedTransaction2Client.DistributedTransactionClient tranclient2 = new DistributedTransaction2Client.DistributedTransactionClient(); using (TransactionScope transcope = new TransactionScope()) { try { Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted); tranclient.Add(); tranclient2.Add(); transcope.Complete(); } catch (Exception err) { Transaction.Current.Rollback(); } } static void Current_TransactionCompleted(object sender, TransactionEventArgs e) { if (e.Transaction.TransactionInformation.Status == System.Transactions.TransactionStatus.Committed) Console.WriteLine(e.Transaction.TransactionInformation.DistributedIdentifier); }
客户端使用TransactionScope类来进行环境事务的设置,这样就很方便知道事务的执行范围,在TransactionScope里面我们可以通过Transaction.Current获取到当前上下文的事务对象,由于事务对象是存储在线程独立存储区里的,所以跨线程访问是没用的,通过依赖事务进行传递。[王清培版权所有,转载请给出署名]