1

There is a console app where I have a sql select statement with some inner joins.I want for every result of this statement to update a column of one of these tables with a new value using C#.

Here what I've already tried:

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

namespace MyProgram
{
    class Program
    {
        private static SqlConnection _connection;
        private static SqlTransaction _transaction;

        static void Main(string[] args)
        {
            using (var connection = new SqlConnection())
            {
                try
                {
                    connection.ConnectionString = ConfigurationManager.ConnectionStrings["myConfig"].ConnectionString;
                    connection.Open();
                    _connection = connection;

                    using (var command = connection.CreateCommand())
                    {
                            command.CommandText =
                            "My Select sql stament with inner joins";

                        using (var reader = command.ExecuteReader())
                        {
                            var indexOfColumn3 = reader.GetOrdinal("IDExtObject");

                            while (reader.Read())
                            {
                                _transaction = _connection.BeginTransaction("UpdateTransaction");
                                command.Transaction = _transaction;

                                var extId = reader.GetValue(indexOfColumn3).ToString();

                                string finalId = "Something new...";

                                try
                                {
                                    UpdateIdSqlTransaction(extId, finalId);
                                    _transaction.Commit();
                                }
                                catch (Exception)
                                {
                                    _transaction.Rollback();
                                }
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    if (_transaction != null)
                        _transaction.Rollback();
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                    {
                        connection.Close();
                    }
                }
            }

            Console.ReadLine();
        }

        private static void UpdateIdSqlTransaction(string objectId, string newId)
        {
            using (_connection)
            {
                SqlCommand command = _connection.CreateCommand();
                command.Connection = _connection;

                var commandText = "The update SQL statement...";
                command.CommandText = commandText;
                command.Parameters.AddWithValue("@ID", objectId);
                command.Parameters.AddWithValue("@newId", newId);
                command.ExecuteNonQuery();
            }
        }
    }
}

The problem is that I am getting this exception:

{"New transaction is not allowed because there are other threads running in the session."}

What is the problem and how can I achieve this?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Giannis Grivas
  • 3,374
  • 1
  • 18
  • 38
  • can you put this "command.Transaction = _transaction;" before ExecuteReader statement ? – FakeisMe Sep 19 '16 at 11:08
  • Why do you need a transaction here? If your update statement is a single statement, it either failed or passed. How many records you expect to process? You may read all Ids and then run an update loop. Can you calculate a new value on the server? In this case you can run the update as a single statement. – Sergey L Sep 19 '16 at 11:16
  • Look @SergeyL I want to start the process and if any of the record is failed I want the whole transaction to rollback. – Giannis Grivas Sep 19 '16 at 11:20
  • It looks there are lot of stuff you would need to fix. 1. Make Command object in UpdateIdSqlTransaction use the same transaction created in Main function. 2. Move While loop reading the reader inside try block (just before call to UpdateIdSqlTransaction function). This eliminate the extra rollback you have. 3. Also you can do better with a single try catch block than using two try catch block. – The Shooter Sep 19 '16 at 11:27

2 Answers2

3

You need to close the existing connection that you are using to Read the data. DataReader is a read only stream of data.

Do it as given below:

  • First read the data and store it in a variable. Close the connection that DataReader is using to read the data.
  • Now use the loop and create a transaction to update the data using the same transaction.
  • Once updated, you can commit the transaction.

Change your code as per the instructions given. Thanks

Check this link for more details on SO

Try the following code. Note that I have not run it as I do not have database set up for the code. Hope it helps.

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

namespace MyProgram
{

    class Item
    {
        public string OldValue { get; set; }
        public string NewValue { get; set; }
    }

    class Program
    {
        //private static SqlConnection _connection;

        private static string connectionString = ConfigurationManager.ConnectionStrings["myConfig"].ConnectionString;

        static void Main(string[] args)
        {
            List<Item> items = new List<Item>();
            ReadData(ref items);

            UpdateIdSqlTransaction(items);

            Console.ReadLine();
        }

        private static void ReadData(ref List<Item> items)
        {
            using (var connection = new SqlConnection())
            {
                connection.ConnectionString = connectionString;
                connection.Open();
                //_connection = connection;

                using (var command = connection.CreateCommand())
                {
                    command.CommandText =
                    "My Select sql stament with inner joins";

                    using (var reader = command.ExecuteReader())
                    {
                        var indexOfColumn3 = reader.GetOrdinal("IDExtObject");

                        while (reader.Read())
                        {
                            var extId = reader.GetValue(indexOfColumn3).ToString();
                            string finalId = "Something new...";

                            items.Add(new Item() { OldValue = extId, NewValue = finalId });
                        }
                    }
                }
            }
        }

        private static void UpdateIdSqlTransaction(List<Item> items)
        {
            SqlTransaction transaction;
            using (var connection = new SqlConnection())
            {
                connection.ConnectionString = connectionString;
                connection.Open();

                using (SqlCommand command = connection.CreateCommand())
                {
                    command.Connection = connection;
                    transaction = connection.BeginTransaction("UpdateTransaction");
                    command.Transaction = transaction;
                    try
                    {
                        foreach (var item in items)
                        {
                            var commandText = "The update SQL statement...";
                            command.CommandText = commandText;
                            command.Parameters.AddWithValue("@ID", item.OldValue);
                            command.Parameters.AddWithValue("@newId", item.NewValue);
                            command.ExecuteNonQuery();
                        }
                        transaction.Commit();
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                        //Log the exception here. To know, why this failed.
                    }
                }
            }
        }
    }
}
Community
  • 1
  • 1
vivek
  • 1,595
  • 2
  • 18
  • 35
1

The issue here is you are trying to read and update at the same time. Within the datareader you are calling update function which is not allowed to maintain the DB in a consistent state.

Here is a bit modified code, where arraylist is used to store data from reader and then loop through to call update function.

class Program
{
    private static SqlConnection _connection;
    private static SqlTransaction _transaction;
    private static ArrayList array;



    static void Main(string[] args)
    {
          _connection = new SqlConnection(ConfigurationManager.ConnectionStrings["myConfig"].ConnectionString);

          try
          {

              using (_connection)
              {

                  string finalId = "Something new...";
                  var command = _connection.CreateCommand();
                  command.CommandText = "your query";
                  _connection.Open();

                  array = new ArrayList();

                  using (var reader = command.ExecuteReader())
                  {
                      var indexOfColumn3 = reader.GetOrdinal("IDExtObject");

                      while (reader.Read())
                      {

                          var extId = reader.GetValue(indexOfColumn3).ToString();


                          array.Add(extId);


                      }
                  }

                  foreach (string id in array)
                  {


                      UpdateIdSqlTransaction(id, finalId);

                  }

              }
          }

          catch (Exception)
          {

          }
            finally
            {
                if (_connection.State == ConnectionState.Open)
                {
                    _connection.Close();
                }
            }



        Console.ReadLine();
    }

    private static void UpdateIdSqlTransaction(string objectId, string newId)
    {
       try
          {

              if (_connection.State == ConnectionState.Closed)
              {
                  _connection.Open();
              }
            SqlCommand command = _connection.CreateCommand();
            command.Connection = _connection;

            _transaction = _connection.BeginTransaction("UpdateTransaction");
            command.Transaction = _transaction;

            var commandText = "your update statement";
            command.CommandText = commandText;
            command.Parameters.AddWithValue("@ID", objectId);
            command.Parameters.AddWithValue("@newId", newId);
            command.ExecuteNonQuery();

            _transaction.Commit();
         }
         catch (Exception)
                        {
                            _transaction.Rollback();
                        }
          finally
            {
                if (_connection.State == ConnectionState.Open)
                {
                    _connection.Close();
                }
            }

    }
}
Nagashree Hs
  • 843
  • 6
  • 18