In my" . NetBrief Discussion on the nature of transactions"In this article, we understand the transaction model as a whole. In our mind, we can have a global transaction processing structure, eliminate the dependency on database transactions, and re-understand the transaction programming model.
Today's article Article We will use. net C # for transactional programming, starting from simple and simple local transactions, that is, the most used ADO. net transaction processing, and then we gradually expand the scope of transaction processing, including the use of distributed transactions and the use of multi-threaded transactions.
Database Transaction Processing We are familiar with database transaction processing, begin transaction ...... End transaction, the Operation package to be transactional in Code Section, in order to make it easier for the article to explain in a organized manner, I will introduce a simple small example here to facilitate comparative analysis with the code below.
Example
1
: We create two tables in the database, that is, a simple column of information. Table 1: Test
Table 2: Test2
The purpose is to conceal the features of the transaction. Therefore, we set the name column of Table 1test as the primary key here. We will intentionally cause duplicate primary keys, resulting in automatic transaction rollback. Let me explain what the two tables are used. Table test is used to intentionally cause internal transaction processing errors. Table Test2 is used to play the role of normal data insertion without errors in transaction processing. I will insert data first in test2, then, when data is inserted in test, an internal transaction execution error is triggered, resulting in transaction rollback. Good we write T-SQL:
Insert into test values ('20170101') -- we insert a record gobegin transaction trbegin trybegininsert into Test2 values ('20170101') into test values ('20160301') in the test table ') -- inserting this row will cause a primary key conflict, that is, the expected result endcommit transaction trend trybegin catchprint 'transaction execution error! 'Print error_number () rollback transaction tr end catch
Let's run and see the result:
In the transaction processing process, it is obvious that the First insert statement is successfully executed, but the data remains unchanged because the second insert statement causes the transaction to roll back. This example may be too simple, and the real enterprise-level application may be very complicated. But our goal is to look at the use of transactions, the simpler the better. [Wang qingpei has all rights reserved. For more information, please sign it.]
Ado. net
Transaction Processing Next we will implement the transaction in AOD. Net of. Net to see the effect.
Example 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 ();}}}
This is a typical ADO. net transaction processing code, in fact, and the T-SQL code in our first example is similar, through ADO.. Net sqlconnection. begintransaction () gets a reference to the database transactions in the underlying ODBC. In fact, there is no real design yet. the transaction processing code in. NET is only a remote call to the database management system. transactions are processed remotely through remote message communication.
Transaction information display class, to facilitate observation of the transaction status information.
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 ();}}
CommittabletransactionTransaction Processing
From here, we will be able to get started with the transaction processing in. net, and learn how the transactions in. Net affect the transaction processing in the remote database management system. In fact, transaction processing is a very complex technical field, and many reversible technical implementations need to be considered. We just have a simple understanding of the principles and basic application. In my
"
. Net
Brief Discussion on the nature of transactions
" The transfer principle of transactions is described in this article. What does transaction transfer mean. In fact, the transaction transfer generally refers to extending the transaction execution scope to other machines through network transmission. For example, we are in. if you execute a transaction operation in. net, we include database operations in this operation. At this time, a series of database operations should be within the scope of transactions, when the transaction is rolled back, the data in the database should be rolled back. However, we cannot always display the execution of ADO. the begintransaction in. NET is acceptable for local transaction processing, that is, for a single resource manager. If operations by multiple resource managers are involved in the transaction scope, this is the scope of distributed transaction processing. Therefore, transaction processing requires seamless service-oriented transaction processing across network transmission. The database management system may assume the role of the Transaction Manager or the role of the resource manager. Too much theoretical knowledge I will not talk about here. Let's look at the code. Next, we use the transactions in. Net to automate database transactions. Let the database automatically perceive that we are performing transactional operations.
Example
3
: We can use the subcategory of the transaction class committabletransaction to submit the transaction class for transaction programming.
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); // The Connection operation must be regarded as a transactional 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 (); // An error occurred while performing the rollback operation }}
The data source connection object represents a remote database resource. Therefore, before performing the operation, we need to add the resource manager to the local Transaction Manager for later voting and submission management.
Enterpriseservice (COM +)
Automated transaction processing In. net2.0 Program Set is not too valued, system. enterpriseservices. DLL. The Assembly is. net in order to use the early COM + technology hosting Assembly, we can use this Assembly to write some COM + applications that we previously could not write. As for the introduction of COM + applications, there is a lot of information on the Internet. I have a good impression that I wrote an article by instructor Pan aiming pan, is to introduce all the features of COM +. Let's continue to process the transaction. Next we will look at how to use the system. enterpriseservices. Transaction class to automate the transaction processing.
Example
4
:
[system. runtime. interopservices. comvisible (true)] // COM + is developed on the basis of COM. the type in the. NET assembly is public as a COM component. [System. enterpriseservices. transaction (transactionoption. required)] // always requires the transaction processing domain public class Test2: servicedcomponent {public Test2 () {} sqlconnection conn = new sqlconnection ("Data Source = .; initial catalog = datamedicine; Integrated Security = sspi "); [AutoComplete (true)] // if no error occurs in the method's execution body class, the 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 () ;}}
DependenttransactionCross-thread Transaction Processing
When writing a high-concurrency program, we will use multiple threads for processing, so that the main thread can have time to process first-line requests, then, the request is distributed to each sub-thread for background processing. Let's look at a picture:
Let us assume that the figure above is an internal structure of our system. The main task of the main thread is to accept external requests, and then put the specific task to one or two child threads to complete, however, there is no inevitable relationship between the sub-thread and the sub-thread. Since the transaction context is not exaggerated, how can we concatenate two or more threads in a transaction. [Wang qingpei has all rights reserved. For more information, please sign it.] Let's look at the dependent transaction processing and the Code:
Example
5
:
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 = 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 ();}}}
We use a sub-thread to execute another transaction. because it depends on transaction processing, the main transaction needs to wait for the results of the sub-transaction processing after the processing is completed. In fact, this example involves the scope of Distributed Transaction processing. When there is more than one resource manager in the transaction scope, the local Transaction Manager will be automatically upgraded to the DTC manager, let's take a look at distributed transaction processing.
DTC (
Distributed Transaction Coordinator)Distributed Transaction Processing Distributed transactions are often used and must be used in development. Data must be synchronized, context updates, and so on. The complexity of different distributed transactions varies according to the usage method. The multi-Resource Manager based on local transactions and the SOA-Based Multi-resource manager for services are also different. Because the local transaction processing is based on the local Transaction Manager, it cannot manage distributed transactions. Once the transaction scope to be processed is extended and accessed by machines, then the local Transaction Manager is automatically upgraded to the Distributed Transaction Manager (DTC ).
Example
6
:
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 ();}}}
Once distributed transaction processing is enabled, we can see it on the DTC manager on our computer.
Check whether your DTC service is enabled.
Based on
WCF
Distributed Transaction Processing of the framework In fact, it is really easy to develop distributed transactions based on the WCF framework. It can well perceive whether the current context is a transaction domain and can serialize transactions to the Service side. However, designing a High-Performance Distributed Transaction Processing Framework is not easy and requires a long time of accumulation and practice. Let's take a look at how distributed transactions are processed in WCF.
Example
7
:
// Servicecontract: [servicecontract (sessionmode = sessionmode. Required)] public interface idistributedtransaction {[transactionflow (transactionflowoption. Allowed)] [operationcontract] void add ();}
// Service Class 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}
// Service Class 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 Configuration:
<Service name = "serverconsole. transaction. distributedtransactionservice1 "behaviorconfiguration =" metadatabehaviors ">
Binding Configuration:
<Bindings> <nettcpbinding> <binding name = "tranbinding" transactionflow = "true" transactionprotocol = "wsatomictransactionoctober2004"> <reliablesession enabled = "true" ordered = "true"/> </ binding> </nettcpbinding> </bindings>
We need to open the transaction Stream Transfer of binding.
Client code:
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 );}
The client uses the transactionscope class to set environmental transactions. This makes it easy to know the transaction execution scope. In transactionscope, we can use transaction. current gets the transaction object in the current context. Because the transaction object is stored in the independent storage area of the online program, cross-thread access is useless and is passed through dependent transactions.[Wang qingpei has all rights reserved. For more information, please sign it.]
The article is over here. From the local transaction, multi-Resource Manager distributed transaction, and SOA distributed transaction, we can have a basic understanding. The above examples are all strictly tested.