Здравствуйте.
ИМХО вместо явного использования метода SqlTransaction.Rollback, гораздо удобнее оборачивать транзакцию в блок using. По выходе из блока using, если транзакция не была commited она откатывается автоматически. По моему такой подход гораздо менее громоздок.
Если вы не сочтете это покушением на святое, я бы зделал еще следующие изменения:
1. Я бы использовал вместо динамческой генерации sql, параметры. Это спасает от всяких бяк типа sql injection + код в таком случае выглядит нагляднее + теоретическая прибавка к performance (ADO.NET исполняет все запросы через sp_executesql, которая умеет кешировать план запроса)
2. Все IDisposable переменные метода теоритически должны быть засунуты в using. Правда это не всегда имеет практический смысл, но хуже точно не будет

.
И чуть-чуть по мелочи:
1. В C# для комментирования существуют уже готовые конструкции <summary>, <remarks>, <param>, <exception> и т.д. По-моему они удобнее + среда потом по ним еще и подсказки показывает.
2. Вместо for в данном случае, по-моему удобнее использовать foreach.
3. SqlCommand.CommandType property всегда по умолчанию CommandType.Text.
Короче у меня примерно так получилось:
/// <summary>
/// Записать все записи из памяти в БД одной транзакцией!
///
/// В случае успеха все записи добавляются в таблицу cdr_records,
/// а имя файла добавляется в таблицу cdr_files (COMMIT)
///
/// В случае неудачи - ничего не происходит и делается запись в журнал (ROLLBACK)
/// </summary>
/// <param name="shortFilename">имя CDR-файла</param>
/// <param name="cdrRecordsList">массив записей, прочитанных из CDR-файла</param>
static void WriteRecordsToDB(string shortFilename, List<CdrRecord> cdrRecordsList)
{
try
{
// вариант с using(...) из примера MSDN
using (SqlConnection connect = new SqlConnection(DBCONNECTION_STRING))
using (SqlCommand cmd = new SqlCommand("INSERT INTO cdr_records (data_type, field_a, field_b, field_c, field_d) values (@data_type, @field_a, @field_b, @field_c, @field_d)", connect))
using (SqlCommand cmd2 = new SqlCommand("INSERT INTO cdr_files (fname) VALUES (@fname)"))
{
cmd.Parameters.Add("@data_type", SqlDbType.VarChar, 666);
cmd.Parameters.Add("@field_a", SqlDbType.VarChar, 666);
cmd.Parameters.Add("@field_b", SqlDbType.VarChar, 666);
cmd.Parameters.Add("@field_c", SqlDbType.VarChar, 666);
cmd.Parameters.Add("@field_d", SqlDbType.VarChar, 666);
cmd2.Parameters.Add("@fname", SqlDbType.VarChar, 666).Value = shortFilename;
// установить соединение с SQL сервером - может бросить исключение!
connect.Open();
using (SqlTransaction transaction = connect.BeginTransaction("MyTransaction"))
{
cmd.Transaction = transaction;
cmd2.Transaction = transaction;
// записать все записи из массива в БД
foreach(CdrRecord rec in cdrRecordsList)
{
cmd.Parameters["data_type"].Value = rec.data_type;
cmd.Parameters["@field_a"].Value = rec.field_a;
cmd.Parameters["@field_b"].Value = rec.field_b;
cmd.Parameters["@field_c"].Value = rec.field_c;
cmd.Parameters["@field_d"].Value = rec.field_d;
cmd.ExecuteNonQuery();
}
cmd2.ExecuteNonQuery();
// попытаться завершить транзакцию
transaction.Commit();
}
}
}
catch (Exception ex)
{
// перехват connect.Open(), BeginTransaction()
LogMessage("WriteRecordsToDB(): " + shortFilename + " is not processed.", ex);
}
}