Re: Грамотно обработать исключения при работе c SQL Server
От: kostya.misura  
Дата: 10.01.07 14:58
Оценка: 75 (7)
Здравствуйте.

ИМХО вместо явного использования метода 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);
    }
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.