Note that there are some explanatory texts on larger screens.

plurals
  1. POASP .NET WebMethods Synchronization
    primarykey
    data
    text
    <p>I have a webmethod in asp.net .asmx service, wich is supposed to check if there are records in a DB, and if there are no records it should add a record</p> <p>the simplified example of code is like this:</p> <pre><code>object Mutex = new object(); [WebMethod] public void InsertIfNotExists(string CLI) { lock (Mutex) { using (SqlConnection conn = new SqlConnection(ConnectionString)) using (SqlDataAdapter adapter = new SqlDataAdapter()) using (DataSet ds = new System.Data.DataSet()){ { //I log with log4net logger.Debug("InsertIfNotExists: Start Function: CLI:" + CLI); int dummy = 0; string sql = "SELECT * CLI Promote where CLI=" + CLI + " "; adapter.SelectCommand = new SqlCommand(sql, conn); adapter.Fill(ds); DataTable t = ds.Tables[0]; logger.Debug("InsertIfNotExists: " + t.Rows.Count + " records found for CLI:" + CLI); if (t.Rows.Count == 0) { logger.Debug("InsertIfNotExists: starting to add to table: CLI:" + CLI); DataRow dr = t.NewRow(); dr["CLI"] = CLI; dr["DateOfSend"] = DateTime.Now; InsertToTable(t, dr, sql); logger.Debug("InsertIfNotExists: added to table: CLI:" + CLI + ", starting re-check"); //checking if exist more then one lines - one more time sql = "SELECT * CLI Promote where CLI=" + CLI + ""; adapter.SelectCommand = new SqlCommand(sql, conn); adapter.Fill(ds); t = ds.Tables[0]; logger.Debug("InsertIfNotExists: re-check for CLI:" + CLI + ", records count=" + t.Rows.Count); } logger.Debug("InsertIfNotExists: Finish Function for CLI:" + CLI); } } } </code></pre> <p>Actually it does more checks and logic, that's why I implement it in .net and not in the SQL statement itself, but essentially that is it.</p> <p>Most of the time the code works well, but sometimes I get into race conditions because of multithreading, though I use lock. </p> <p>Sample output I got today:</p> <pre><code>2013-09-15 11:47:14,145 [21] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567 2013-09-15 11:47:14,145 [13] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567 2013-09-15 11:47:14,148 [21] DEBUG Namespace.Service1 InsertIfNotExists: 0 records found for CLI: 0501234567 2013-09-15 11:47:14,148 [21] DEBUG Namespace.Service1 InsertIfNotExists: starting to add to table: CLI: 0501234567 2013-09-15 11:47:14,148 [13] DEBUG Namespace.Service1 InsertIfNotExists: 0 records found for CLI: 0501234567 2013-09-15 11:47:14,148 [13] DEBUG Namespace.Service1 InsertIfNotExists: starting to add to table: CLI: 0501234567 2013-09-15 11:47:14,149 [21] DEBUG Namespace.Service1 InsertIfNotExists: added to table: CLI: 0501234567, starting re-check 2013-09-15 11:47:14,149 [13] DEBUG Namespace.Service1 InsertIfNotExists: added to table: CLI: 0501234567, starting re-check 2013-09-15 11:47:14,154 [27] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567 2013-09-15 11:47:14,157 [27] DEBUG Namespace.Service1 InsertIfNotExists: 2 records found for CLI: 0501234567 2013-09-15 11:47:14,157 [27] DEBUG Namespace.Service1 InsertIfNotExists: Finish Function for CLI: 0501234567 2013-09-15 11:47:14,183 [13] DEBUG Namespace.Service1 InsertIfNotExists: re-check for CLI: 0501234567, records count=2 2013-09-15 11:47:14,184 [21] DEBUG Namespace.Service1 InsertIfNotExists: re-check for CLI: 0501234567, records count=2 2013-09-15 11:47:14,185 [13] DEBUG Namespace.Service1 InsertIfNotExists: Finish Function for CLI: 0501234567 2013-09-15 11:49:19,626 [21] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI:0507654321 </code></pre> <p>What we see here that 3 threads attempted to insert CLI 0501234567 to the table in the parallel. Threads 21 and 13 entered the race conditons and each one inserted 1 record. Then thread 27 also tried to insert a record, but found existing records and exited.</p> <p>Why did they do it when locked on mutex?</p> <p>Note: Thread 21 never gets to Finish - I think it is caused by an exception in thread 21, because I try to remove the "additional" rows after the re-check in the real function, and then the second thread which tries to do it should get into an exception. I know it's ugly but that's the only solution I got for now, I'd like to know how to do it properly.</p> <p>Why does asp.net behave in such a way and what is the proper way to accomplish that task without the race conditions?</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload