nesto o

Upload: djomla

Post on 03-Apr-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/28/2019 Nesto o

    1/10

    Using transactions inADO.NET

    Posted byJohn Charles Olamendy in Articles| ADO.NET inC#on March 13, 2010Tags:ado.net, Using transactions in ADO.NETIn this article, I will cover the main principles and techniquesconcerning transactions using ADO.NET as the data accesstechnology and SQL Server as the Database system in order tobuild robust enterprise information systems.

    Introduction

    Information is critical in today's information age, but we need to keep theinformation as consistent with the reality as possible. Database systems holds dataand ADO.NET enables to access this data in the backend system, and in order to

    keep the data consistent while we access the data using ADO.NET, we need to usetransactions. A transaction is a set of operations (enabling data interchange between

    the business entities) where all of them must be successful or fail to ensureconsistency on the behavior of the system. A transaction is said to perform a "unit of

    work" because it does all the work required to update the database to reflect the

    real-world changes. In this article, I will cover the main principles and techniquesconcerning transactions using ADO.NET as the data access technology and SQL

    Server as the Database system in order to build robust enterprise information

    systems.

    ACID Properties of Transactions

    Transactions are characterized by four properties called ACID properties. To pass thisACID test, a transaction must be Atomic, Consistent, Isolated, and Durable.

    Atomic. All steps in the transaction should succeed or fail together. If a

    transaction successfully completes and the system agrees to preserve its

    effects, we say that the transaction has committed, otherwise the transactionhas aborted, and the underlying changes made to the database are undone,

    or rolled back.

    Consistency. The transaction takes the database from a stable state into a

    new stable state. The database must satisfy the business rules of the real-world enterprise it models, thus the execution of a transaction must maintain

    all these consistency constraints.

    Isolated. Every transaction is an independent entity. The execution of one

    transaction does not affect the execution of other transactions running at thesame time.

    Durability. The results of committed transactions are permanent.

    http://www.c-sharpcorner.com/authors/john_charles/john-charles-olamendy.aspxhttp://www.c-sharpcorner.com/Articles/http://www.c-sharpcorner.com/1/131/ado-net-in-C-Sharp.aspxhttp://www.c-sharpcorner.com/1/131/ado-net-in-C-Sharp.aspxhttp://www.c-sharpcorner.com/tags/ado.nethttp://www.c-sharpcorner.com/tags/Using-transactions-in-ADO.NEThttp://www.c-sharpcorner.com/authors/john_charles/john-charles-olamendy.aspxhttp://www.c-sharpcorner.com/Articles/http://www.c-sharpcorner.com/1/131/ado-net-in-C-Sharp.aspxhttp://www.c-sharpcorner.com/1/131/ado-net-in-C-Sharp.aspxhttp://www.c-sharpcorner.com/tags/ado.nethttp://www.c-sharpcorner.com/tags/Using-transactions-in-ADO.NET
  • 7/28/2019 Nesto o

    2/10

    Transactions in enterprise systems

    Modern relational database management systems support transactions such asOracle database and Microsoft SQL Server. Data access API such as ODBC, JDBC.

    OleDB and ADO.NET enable developer use transactions in their applications. If thedeveloper is executing transactions against several distributed data sources, then the

    Microsoft Distributed Transaction Coordinator (MSDTC) is used. MSDTC along withCOM+ are middleware which enables the execution of distributed transactions. Thereare a lot of software packages to assist developers to write and execute distributed

    transactions ensuring the ACID properties across all the underlying data sources,

    using mechanisms such as two-phase commit and rollback.

    Transactions in ADO.NET

    ADO.NET supports single-database transactions as well as distributed transactions.Single-database transaction model is implemented using the underlying .NET

    managed providers for Transaction and Connection classes from the System.Datanamespace. Distributed transaction model is implemented using classes in the

    namespace System.Transactions.

    The following code snippet illustrates how to implement single-database transactionin ADO.NET (see Listing 1).

    using System;

    using System.Data;using System.Data.SqlClient;

    using System.Collections.Generic;using System.Text;

    namespace TransactionExampleCons

    { classProgram

    { staticvoid Main(string[] args)

    {

    string strConnString = "myconnectionstring"; SqlTransaction objTrans = null;

    using (SqlConnection objConn = newSqlConnection(strConnString)){

    objConn.Open();objTrans = objConn.BeginTransaction();

    SqlCommand objCmd1 = newSqlCommand("insert into tbExamplevalues(1)", objConn);

    SqlCommand objCmd2 = newSqlCommand("insert into tbExamplevalues(2)", objConn);

    try{

    objCmd1.ExecuteNonQuery();objCmd2.ExecuteNonQuery();

  • 7/28/2019 Nesto o

    3/10

    objTrans.Commit();}

    catch (Exception){

    objTrans.Rollback();}

    finally{

    objConn.Close();

    }

    }}

    }}

    Listing 1

    You can also use transactions along with DataSet and DataAdapter objects. The main

    idea is to set the created transactions to every command of the DataAdapter (see

    Listing 2).

    using System;

    using System.Data;using System.Data.Common;

    using System.Data.SqlClient;using System.Collections.Generic;

    using System.Text;using System.Reflection;

    namespace OLA.Framework.Data.SqlClient.Transactions

    {

    publicclassSQLDataAdapter_TransactionalManagement{

    publicSqlTransaction BeginTransaction(object objTableAdapter){

    returnthis.BeginTransaction(objTableAdapter, IsolationLevel.ReadCommitted);

    }

    publicSqlTransaction BeginTransaction(object objTableAdapter, IsolationLevel i

    sLevel){

    Type taType = objTableAdapter.GetType(); SqlConnection objConn = this.prvGetConnection(objTableAdapter);

    if(objConn.State == ConnectionState.Closed){

    objConn.Open();}

  • 7/28/2019 Nesto o

    4/10

    SqlTransaction stTrans = objConn.BeginTransaction(isLevel);

    this.prvSetTransaction(objTableAdapter, stTrans);

    return stTrans;}

    publicvoid EnlistInTransaction(object objTableAdapter, SqlTransaction stTrans)

    { this.prvSetTransaction(objTableAdapter, stTrans);

    }

    privateSqlConnection prvGetConnection(object objTableAdapter)

    { SqlConnection scResult = null;

    Type taType = objTableAdapter.GetType(); PropertyInfo prtConnection =

    taType.GetProperty("Connection",BindingFlags.NonPublic |BindingFlags.Instance);scResult = (SqlConnection)prtConnection.GetValue(objTableAdapter,null);

    return scResult;}

    privatevoid prvSetConnection(object objTableAdapter, SqlConnection objConn){

    Type taType = objTableAdapter.GetType(); PropertyInfo prtConnection =

    taType.GetProperty("Connection", BindingFlags.NonPublic |BindingFlags.Instance);prtConnection.SetValue(objTableAdapter, objConn, null);

    }

    privatevoid prvSetTransaction(object objTableAdapter, SqlTransaction stTrans)

    { Type taType = objTableAdapter.GetType();

    PropertyInfo adapterProperty =taType.GetProperty("Adapter", BindingFlags.NonPublic |BindingFlags.Instance);

    SqlDataAdapter sdaAdapter =(SqlDataAdapter)adapterProperty.GetValue(objTableAdapter, null);

    sdaAdapter.UpdateCommand.Transaction = stTrans;sdaAdapter.InsertCommand.Transaction = stTrans;

    sdaAdapter.DeleteCommand.Transaction = stTrans;sdaAdapter.AcceptChangesDuringUpdate = false;

    PropertyInfo prtCommandCollection =

    taType.GetProperty("CommandCollection",BindingFlags.NonPublic| BindingFlags.Instance);

    SqlCommand[] arrCommands =(SqlCommand[])prtCommandCollection.GetValue(objTableAdapter, null);

    foreach (SqlCommand objCmd in arrCommands){

  • 7/28/2019 Nesto o

    5/10

    objCmd.Transaction = stTrans;}

    this.prvSetConnection(objTableAdapter, stTrans.Connection);}

    }}

    Listing 2

    The AcceptChangesDuringUpdate property on the DataAdapter object when it's set to

    false, it specifies that the DataAdapter object should not change the state of datarows as long as it's executing the commands on several rows, thus the developer

    have to explicitly call the AcceptChanges method on the underlying DataSet object atthe end of the transaction. If you set the AcceptChangesDuringUpdate property to

    true, and one row of all the rows to be updated (let's suppose this row is the lastone) is not consistent with the rules of the database; when the transaction is

    executed and then it's rolled back, all the rows but the last one (the row with invalidvalues) has their state changed to UnModified. In .NET 1.1, a partial solution to this

    new problem is to extract the rows to be updated into a smaller DataSet using the

    GetChanges method of DataSet class. If the transaction commits, you could refreshthe data and merge the new and fresh data from the database into the DataSet. Onedrawback with this approach is that the GetChanges and Merge methods are very

    expensive.

    Let's see how to use the class in the Listing 2 in a real-world example (see Listing 3).

    SqlTransaction stTrans = null;try

    { this.Validate();

    this.m_bsEmployee.EndEdit();//Accept changes through this BindingSource

    //instance

    SQLDataAdapter_TransactionalManagement sdatmInstance= newSQLDataAdapter_TransactionalManagement();

    stTrans = sdatmInstance.BeginTransaction(this.m_taEmployee);//Set//transaction for the table adapter in order to update the Employee table. //You can

    extend this transaction scope to several table adapters this.m_taEmployee.Update(this.m_hR_DataSet.Employee);//Update the

    //changes in the this.m_hr_DataSet.Employee object into the Employee table//through the table adapter

    stTrans.Commit();//Commit changes to the database system, if everything //is OK this.m_hR_DataSet.Employee.AcceptChanges();//Commit the changes to the

    //dataset object}

    catch (SqlException ex){

    stTrans.Rollback();//Cancel the changes to the database system, if an //erroroccurs

    System.Windows.Forms.MessageBox.Show(ex.Message, "SQLException",MessageBoxButtons.OK, MessageBoxIcon.Information);//Show the error

    message

  • 7/28/2019 Nesto o

    6/10

    }catch (Exception ex)

    {System.Windows.Forms.MessageBox.Show(ex.Message, "Error

    Message",MessageBoxButtons.OK, MessageBoxIcon.Information);}

    finally{

    stTrans.Dispose();//Release the resources associated to the transaction //object

    }

    Listing 3

    Distributed Transactions

    Developers can also deal with database systems in which a transaction can access a

    heterogeneous set of transaction processing systems at multiple sites (perhapsscattered throughout the world) also known as resource managers. This sort of

    transactions is known as distributed transactions. These systems process someoperations within the current transactions (a subtransaction) and then report a

    success or a failure. In addition to resource managers, we need a middlewarepackage that listens to and coordinates the final result between resource managers.

    This middleware is known as transaction manager.

    The transaction manager that ships with Windows is the Microsoft DistributedTransaction Coordinator (MSDTC). You can consume the services provided by MSDTC

    by using MTS/COM+, System.EnterpriseServices and the new System.Transactionsnamespace in .NET 2.0.

    In order to implement an atomic commit, we need a protocol for the communication

    of resource managers and transaction managers. A lot of atomic commit protocols

    have been proposed, but the one that is in common use is called two-phase commitprotocol. This protocol is initiated by the transaction manager or coordinator whenthe underlying transaction requests to commit. To perform correctly this protocol,

    the coordinator needs to know the identities of all the resource managers involved inthe transaction. Thus, each time a resource manager joins the transaction; its

    identification is sent to the coordinator.

    The two-phase commit protocol comprises two phases. The first phase involves

    preparing the changes required for the commit. The purpose is to determine whetherresource managers are willing and ready to commit, but not actually committed yet.

    Once all the other resource managers notify to the transaction manager or

    coordinator to agree to commit, then the coordinator lets the resource managers togo ahead and commit their changes.

    In a distributed transaction, anything, which has the capability to enlist itself in an

    MSDTC transaction, can participate as resource managers.In .NET 1.1, you need to create a component hosted within a class library. This

    library must be in the GAC, thus you need to strongly name it using the sn.exe tool.This component can be implemented by inheriting from the class ServicedComponent

    in the System.EnterpriseServices namespace which provides the programminginterface to access MSDTC functionality. You need to specify the TransactionAttribute

    on top of the component class and declare the transactional behavior using the

  • 7/28/2019 Nesto o

    7/10

    TransactionOption enumeration (see Table 1). Finally, this component will be able toenlist itself in an MSDTC transaction.

    EnumerationDescription

    DisableThis component does not participate in a

    transaction. This is the default value.

    NotSupportedThis component runs outside the context of atransaction.

    SupportedThis component participates in a transactionif this exists. But it does not require a

    transaction or create a new one.

    Required

    This component must have a transaction. If

    a transaction does not exist, then it creates anew one. If a transaction exists, then it

    participates in this one.

    RequiresNewThis component must have a transaction,

    and always creates a new transaction.

    Table 1

    The implementation of the component is shown in Listing 4.

    using System;using System.EnterpriseServices;

    using System.Data;using System.Data.SqlClient;

    using System.Data.OracleClient;using System.Collections.Generic;

    using System.Text;

    namespace TransactionExampleCons

    {[Transaction(TransactionOption.RequiresNew)]

    publicclassCOMPlusDistributedTransaction : ServicedComponent{

    publicstaticvoid FinancialAccount(int nDebitAccount, float fDebitAmount, intnCreditAccount, float fCreditAmount)

    { OracleConnection objOracleConn = null;

    SqlConnection objSqlConn = null;

    OracleCommand cmdDebit = null;

    SqlCommand cmdCredit = null;

    try

    {objOracleConn = newOracleConnection("oracleconnstring");

    objSqlConn = newSqlConnection("oracleconnstring");

    string strDebitCmd = String.Format("UPDATE tbAccount SET amount={0}

    WHERE accountid={1}", fDebitAmount, nDebitAccount);

  • 7/28/2019 Nesto o

    8/10

    string strCreditCmd = String.Format("UPDATE tbAccount SETamount={0} WHERE accountid={1}", fCreditAmount, nCreditAccount);

    cmdDebit = newOracleCommand(strDebitCmd, objOracleConn);

    cmdCredit = newSqlCommand(strCreditCmd, objSqlConn);

    cmdCredit.ExecuteNonQuery();cmdDebit.ExecuteNonQuery();

    } finally

    {cmdDebit.Dispose();

    cmdCredit.Dispose();

    objOracleConn.Close();

    objSqlConn.Close();}

    }

    }}

    Listing 4

    If the method finishes successfully, then the component notifies to the coordinator

    that it's ready and willing with the changes, pending others. If anyone else in thedistributed transaction aborts, then all the bets are off and no changes are

    committed.

    Now let's illustrate how to implement distributed transaction in .NET 2.0 using the

    class in the System.Transactions namespace. In this case, we don't need an instanceof a ServicedComponent-inherited class. We just create an instance of a .NET

    managed class (see Listing 5).

    using System;using System.Transactions;

    using System.Data;using System.Data.SqlClient;

    using System.Data.OracleClient;using System.Collections.Generic;

    using System.Text;

    namespace TransactionExampleCons

    {

    publicclassManagedDistributedTransaction{

    publicvoid FinancialAccount(int nDebitAccount, float fDebitAmount, int nCredit

    Account, floatfCreditAmount)

    { using(TransactionScope tsInstance = newTransactionScope())

    {

    OracleConnection objOracleConn

  • 7/28/2019 Nesto o

    9/10

    = newOracleConnection("oracleconnstring"); SqlConnection objSqlConn = newSqlConnection("oracleconnstring");

    string strDebitCmd = String.Format("UPDATE tbAccount SET amount={0}

    WHERE accountid={1}", fDebitAmount, nDebitAccount); string strCreditCmd = String.Format("UPDATE tbAccount SET

    amount={0} WHERE accountid={1}", fCreditAmount,nCreditAccount);

    OracleCommand cmdDebit = newOracleCommand(strDebitCmd,

    objOracleConn); SqlCommand cmdCredit = newSqlCommand(strCreditCmd, objSqlConn);

    cmdCredit.ExecuteNonQuery();cmdDebit.ExecuteNonQuery();

    tsInstance.Complete();}

    }}

    }

    Listing 5

    You can check the status of the distributed transaction when it's running by going tothe Control Panel | Administrative Tools | Component Services and navigate through

    the tree on the left side to view the transaction list (see Figure 1).

  • 7/28/2019 Nesto o

    10/10

    Figure 1

    Conclusion

    In this article, I've illustrated the main principles of transactions in ADO.NET through

    real-world example. Now, you can adapt these examples in your own businessscenario.