Home All Groups Group Topic Archive Search About

Implement ADO.NET Row Level Pessimistic Locking

Author
31 May 2006 11:15 AM
MSDeveloper
I am trying to implement record level pessimistic locking using ADO.NET
and SQL Server 2000. A reduced structure of the table is shown:

CarrierName(varchar)   CarrierCode(varchar)        RefundStatus(int)
------------------------------------------------------------------------
--------
US Postal Service           USPS                              1
US Postal Service           USPS                              2
Canada Postal Service    CPC                                1  
UK Postal Service           RMG                               1
UK Postal Service           RMG                               2

I have a win form application(C#), which on a button click selects only
the RMG records and then updates these records. The requirement is that,
during this operation these RMG records must be pessimistically locked
so that no other application can update these records during this time,
but the USPS and CPC records should remain updateable.

After browsing through different forums, I realised that ADO.NET does
not support pessimistic concurrency directly but there is a workaround
using transaction. This is how I implemented the solution:

private const string DBConnectionString = "Database=BnBCSO;
Server=10.20.1.19;UID=sa;PWD=rebaca;" + "Connection
Timeout=15;Enlist=False;Max Pool Size=150; Min Pool Size=5;";
private IsolationLevel oIsolationLevel = IsolationLevel.Serializable;
private string SelectStatement = "select * from PackageRefund where
CarrierCode = 'RMG'";

private void Process()
          {
               SqlConnection oConn = null;
               SqlTransaction oTrxn = null;
               SqlDataAdapter oDataAdapter = null;
               DataSet ds = new DataSet();

               try
               {
                    oConn = new SqlConnection( DBConnectionString );
                    oConn.Open();
                    oTrxn = oConn.BeginTransaction( oIsolationLevel );
                    SqlCommand oCommand = new SqlCommand(
SelectStatement, oConn, oTrxn );
                    oDataAdapter = new SqlDataAdapter( oCommand );
                    oDataAdapter.Fill( ds );

                    for( int i = 0; i < ds.Tables[0].Rows.Count; i++ )
                    {
                         Thread.Sleep( 5000 );
                         ds.Tables[0].Rows[i]["RefundStatus"] = 4;
                    }

                    SqlCommandBuilder oCommandBuilder = new
SqlCommandBuilder( oDataAdapter );
                    oDataAdapter.Update( ds );
                    oTrxn.Commit();
               }
               catch( Exception exc )
               {
                    oTrxn.Rollback();
               }
               finally
               {
                    if( oDataAdapter != null )
                    {
                         oDataAdapter.Dispose();
                         oDataAdapter = null;
                    }
                    if( oTrxn != null )
                    {
                         oTrxn.Dispose();
                         oTrxn = null;
                    }
                    if( oConn != null )
                    {
                         oConn.Close();
                         oConn.Dispose();
                         oConn = null;
                    }
               }
          }

          private void cmdStart_Click(object sender, System.EventArgs e)
          {
               try
               {
                    Thread oThread = new Thread( new ThreadStart(
Process ) );

                    oThread.Start();
               }
               catch( Exception exc )
               {
               }
          }

The issue is that after I have started the program, I go to SQL Query
Analyzer and execute these sql statements separately:

update [TABLE] set RefundStatus = 3 where CarrierCode = 'RMG'
update [TABLE] set RefundStatus = 3 where CarrierCode = 'USPS'
update [TABLE] set RefundStatus = 3 where CarrierCode = 'CPC'

If record level pessimistic locking was properly implemented, then the
USPS and CPC updates should have succeded while the RMG one should
wait/timeout. But in this case, all the three update statements are
waiting and eventually timing out, possibly waiting for the lock to be
released. That means that this solution has implemented a table lock
rather than a row lock.

What do I have to change if I want only the selected records to be
locked while the other records in the same table should be updateable.

Thanks.

--
Sent via .NET Newsgroups
http://www.dotnetnewsgroups.com

Author
4 Jun 2006 6:33 PM
Mary Chipman [MSFT]
If you set the isolation level = serializable for a connection, then
it stays in effect until the connection is closed or it is reset to
another isolation level.

Using the default isolation level of read committed ensures that only
records being modified will be affected--SQLS automatically escalates
locks those rows while leaving other rows updateable. If I understand
your code correctly, you are locking all rows at the outset by using
serializable. Both the modified rows and rows not being updated will
be locked until the transaction either commits or is rolled back,
which is what serializable means--that you can re-run a transaction
and it will have the same effect. Simply selecting rows without
updating anything inside of a serializable explicit transaction will
lock those rows until either a commit or a rollback is issued. No
other processes can even read them.

If you search on "SQL Server isolation levels" you will come up with
some good hits that explain what's going on in more detail.

--Mary

On Wed, 31 May 2006 04:15:00 -0700, MSDeveloper <net_for***@yahoo.com>
wrote:

Show quote
>I am trying to implement record level pessimistic locking using ADO.NET
>and SQL Server 2000. A reduced structure of the table is shown:
>
>CarrierName(varchar)   CarrierCode(varchar)        RefundStatus(int)
>------------------------------------------------------------------------
>--------
>US Postal Service           USPS                              1
>US Postal Service           USPS                              2
>Canada Postal Service    CPC                                1  
>UK Postal Service           RMG                               1
>UK Postal Service           RMG                               2
>
>I have a win form application(C#), which on a button click selects only
>the RMG records and then updates these records. The requirement is that,
>during this operation these RMG records must be pessimistically locked
>so that no other application can update these records during this time,
>but the USPS and CPC records should remain updateable.
>
>After browsing through different forums, I realised that ADO.NET does
>not support pessimistic concurrency directly but there is a workaround
>using transaction. This is how I implemented the solution:
>
>private const string DBConnectionString = "Database=BnBCSO;
>Server=10.20.1.19;UID=sa;PWD=rebaca;" + "Connection
>Timeout=15;Enlist=False;Max Pool Size=150; Min Pool Size=5;";
>private IsolationLevel oIsolationLevel = IsolationLevel.Serializable;
>private string SelectStatement = "select * from PackageRefund where
>CarrierCode = 'RMG'";
>
>private void Process()
>          {
>               SqlConnection oConn = null;
>               SqlTransaction oTrxn = null;
>               SqlDataAdapter oDataAdapter = null;
>               DataSet ds = new DataSet();
>
>               try
>               {
>                    oConn = new SqlConnection( DBConnectionString );
>                    oConn.Open();
>                    oTrxn = oConn.BeginTransaction( oIsolationLevel );
>                    SqlCommand oCommand = new SqlCommand(
>SelectStatement, oConn, oTrxn );
>                    oDataAdapter = new SqlDataAdapter( oCommand );
>                    oDataAdapter.Fill( ds );
>
>                    for( int i = 0; i < ds.Tables[0].Rows.Count; i++ )
>                    {
>                         Thread.Sleep( 5000 );
>                         ds.Tables[0].Rows[i]["RefundStatus"] = 4;
>                    }
>
>                    SqlCommandBuilder oCommandBuilder = new
>SqlCommandBuilder( oDataAdapter );
>                    oDataAdapter.Update( ds );
>                    oTrxn.Commit();
>               }
>               catch( Exception exc )
>               {
>                    oTrxn.Rollback();
>               }
>               finally
>               {
>                    if( oDataAdapter != null )
>                    {
>                         oDataAdapter.Dispose();
>                         oDataAdapter = null;
>                    }
>                    if( oTrxn != null )
>                    {
>                         oTrxn.Dispose();
>                         oTrxn = null;
>                    }
>                    if( oConn != null )
>                    {
>                         oConn.Close();
>                         oConn.Dispose();
>                         oConn = null;
>                    }
>               }
>          }
>
>          private void cmdStart_Click(object sender, System.EventArgs e)
>          {
>               try
>               {
>                    Thread oThread = new Thread( new ThreadStart(
>Process ) );
>
>                    oThread.Start();
>               }
>               catch( Exception exc )
>               {
>               }
>          }
>
>The issue is that after I have started the program, I go to SQL Query
>Analyzer and execute these sql statements separately:
>
>update [TABLE] set RefundStatus = 3 where CarrierCode = 'RMG'
>update [TABLE] set RefundStatus = 3 where CarrierCode = 'USPS'
>update [TABLE] set RefundStatus = 3 where CarrierCode = 'CPC'
>
>If record level pessimistic locking was properly implemented, then the
>USPS and CPC updates should have succeded while the RMG one should
>wait/timeout. But in this case, all the three update statements are
>waiting and eventually timing out, possibly waiting for the lock to be
>released. That means that this solution has implemented a table lock
>rather than a row lock.
>
>What do I have to change if I want only the selected records to be
>locked while the other records in the same table should be updateable.
>
>Thanks.

AddThis Social Bookmark Button