Я пытаюсь смоделировать случай, с которым мы сталкиваемся, когда оператор insert
выполняется в многопоточном процессе и вызывает исключения,
class Program
{
private readonly static string[] names = { "name1", "name2", "name3", "name4" };
private const string CreateQuery = @"DROP TABLE IF EXISTS name_multi_thread_test CASCADE;
CREATE TABLE name_multi_thread_test (name VARCHAR(20));";
private const string InsertQuery = @"INSERT INTO name_multi_thread_test VALUES('{0}');";
private const string SelectQuery = "SELECT * FROM name_multi_thread_test;";
static void Main(string[] args)
{
var warehouseHelper = new WarehouseHelper();
try
{
System.Threading.Tasks.Parallel.ForEach(names, name =>
{
//foreach(var name in names)
//{
for(int i = 0; i < 10; i++)
{
warehouseHelper.BeginTransaction();
warehouseHelper.ExecuteNonQuery(CreateQuery);
warehouseHelper.ExecuteNonQuery(string.Format(InsertQuery, name));
using (var reader = warehouseHelper.ExecuteReader(SelectQuery))
{
while (reader.Read())
{
Console.WriteLine(reader["name"]);
}
}
warehouseHelper.CommitTranzaction();
}
}//;
);
}
catch(Exception ex)
{
Console.Write(ex.Message);
}
}
}
class WarehouseHelper
{
private IDbConnection _transactionConnection;
private IDbTransaction _transaction;
public void ExecuteNonQuery(string commandText)
{
var connection = GetConnection();
using (var command = connection.CreateCommand())
{
command.Transaction = _transaction;
command.CommandText = commandText;
command.ExecuteNonQuery();
}
}
public IDataReader ExecuteReader(string commandText)
{
var connection = GetConnection();
using (var command = connection.CreateCommand())
{
command.Transaction = _transaction;
command.CommandText = commandText;
return command.ExecuteReader();
}
}
public void BeginTransaction()
{
_transactionConnection = ConnectionManager.CreateConnection();
_transactionConnection.Open();
_transaction = _transactionConnection.BeginTransaction();
}
public void CommitTranzaction()
{
_transaction.Commit();
_transactionConnection.Close();
_transaction = null;
_transactionConnection = null;
}
private IDbConnection GetConnection()
{
if(_transactionConnection != null)
{
return _transactionConnection;
}else
{
var connection = ConnectionManager.CreateConnection();
connection.Open();
return connection;
}
}
}
class ConnectionManager
{
private static string _connectionStringOdbc = "Driver={Vertica};SERVER=x.x.x.x;PORT=5433;DATABASE=mydb;UID=username;PWD=password;";
public static OdbcConnection CreateConnection()
{
return new OdbcConnection(_connectionStringOdbc);
}
}
, поэтому в основном для каждого имени в names
Я начинаю транзакцию, создаю таблицу, вставляю некоторые записи и читаю их, и это повторяется десять раз подряд в цикле for, что происходит в том случае, если первая итерация выполняется нормально, пока выполнение не достигнет CommitTranzation
, в этом примере мы имеем4 потока, как только первый поток устанавливает _transaction
на ноль, а следующий поток пытается выполнить _transaction.commit()
Я получаю NullReferenceException
Ссылка на объект не установлена на экземпляр объекта.
public void CommitTranzaction()
{
_transaction.Commit();
_transactionConnection.Close();
_transaction = null;
_transactionConnection = null;
}
, как если бы это был один и тот же экземпляр _transaction и _transactionConnection для всех 4 потоков, я знаю, что нам здесь чего-то не хватает в обработке многопоточности, но что это, на данный момент, чтобы разблокировать себя, я переместилwarehouseHelper.BeginTransaction();
оператор вне параллельного выполнения, так что теперь он находится сразу после try{
, а оператор warehouseHelper.CommitTranzaction();
находится вконец блока try{
вне параллельного выполнения также, что является правильным подходом здесь.