Узнать, какой запрос вызвал исключение

У меня есть OleDbCommand для вставок, которые я пытался реализовать, чтобы избежать SQL-инъекций. До этого я использовал простые строки для своих запросов, и мне это не нравилось. Теперь мой кусок кода для вставки записей выглядит так:

 try
 {
    OleDbConnection rConn = new OleDbConnection(args[3]);
    rConn.Open();
    using (OleDbCommand insert = new OleDbCommand(String.Format(Globals.QUERY_INSERT_CLICK, args[4]), rConn))
    {
        insert.Parameters.Add("id", OleDbType.BigInt, 20);
        insert.Parameters.Add("email", OleDbType.VarChar, 255);
        insert.Parameters.Add("clickTime", OleDbType.Date, 20);
        insert.Parameters.Add("subscriberId", OleDbType.BigInt, 20);
        insert.Parameters.Add("link", OleDbType.VarChar, 255);
        insert.Parameters.Add("sendQueueId", OleDbType.BigInt, 20);
        insert.Parameters.Add("mailingListName", OleDbType.VarChar, 255);
        insert.Parameters.Add("newsletterId", OleDbType.BigInt, 20);
        insert.Parameters.Add("sendDate", OleDbType.Date, 20);

        insert.Parameters[0].Value = clickitem.Id;
        insert.Parameters[1].Value = clickitem.Email;
        insert.Parameters[2].Value = clickitem.ClickTime;
        insert.Parameters[3].Value = clickitem.SubscriberId;
        insert.Parameters[4].Value = clickitem.Link;
        insert.Parameters[5].Value = clickitem.SendQueueId;
        insert.Parameters[6].Value = mailingListName;
        insert.Parameters[7].Value = newsletterID;
        insert.Parameters[8].Value = sendDate;

        insert.Prepare();
        insert.ExecuteNonQuery();
    }
    rConn.Close();
}
catch (OleDbException oldbex)
{
    logger.WriteToLog("GETCLICKS", "OleDbException: " + Globals.ERROR_INSERT_CLICK + oldbex.Message);
}
catch (Exception ex)
{
    logger.WriteToLog("GETCLICKS", Globals.ERROR_INSERT_CLICK + ex.Message);
}

У меня есть тысячи вставок, и я вижу из своего журнала, что некоторые из них вставлены неправильно. Исключение говорит мне, например. не может конвертировать из bigint в datetime и тому подобное. Хотя большинство моих записей вставляются правильно, я хочу знать, какой именно из этих запросов на вставку вызвал ошибку. Как я могу это понять?

Н.Б. Перед использованием этого метода у меня был доступ к моей строке запроса, и я сразу же обнаружил ошибку. Теперь я думаю, что мой иммунитет к SQL-инъекциям вызывает у меня некоторое замешательство.


person disasterkid    schedule 08.04.2013    source источник
comment
В вашем catch просто выведите из системы параметры так же, как ранее вы выходили из своей строки SQL.   -  person RB.    schedule 08.04.2013
comment
NB, если ваш пользователь может повлиять на args[4], этот код по-прежнему уязвим для SQL-инъекций.   -  person tomfanning    schedule 08.04.2013
comment
Вам также может быть проще работать с OleDbParameterCollection.AddWithValue(), который не требует указания типа или длины параметра. msdn.microsoft .com/en-us/library/   -  person tomfanning    schedule 08.04.2013


Ответы (4)


Поскольку вы упомянули о получении разных/множественных ошибок преобразования данных, я предлагаю улучшить ведение журнала при обнаружении исключения OleDbException.

Вы можете записать значения каждого из параметров в журнал сразу после начальной записи в журнале 'GETCLICKS'. Это даст вам лучшее представление о том, какое значение, поступающее от пользователя, имеет неверный формат.

person Brian Dishaw    schedule 08.04.2013

Стандартная ошибка SQL не покажет вам столбец или значение, вызвавшее ошибку.

Самый простой способ — добавить оператор SQL и значения параметров к вашему вызову журнала.

string params = string.Join(Environment.NewLine,
                            insert.Parameters
                                  .Select(p => string.Format("{0} : {1}",
                                                             p.Name, 
                                                             p.Value))
                                  .ToArray()
                            );

string message = string.Format("{0}: {1}{2}\n{3}\n{4}",
                               "OleDbException: " ,
                               Globals.ERROR_INSERT_CLICK,
                               oldbex.Message,
                               insert.CommandText,
                               params);

logger.WriteToLog("GETCLICKS", message );
person D Stanley    schedule 08.04.2013

Свойство параметра Value имеет универсальный тип Object. Таким образом, он принимает все, что вы ему назначаете. Конечно, это не лучший способ обработки ваших данных. Я попытаюсь преобразовать значение в соответствующий тип данных для параметра и избежать отправки фиктивных значений в базу данных. Таким образом, вы поймаете ошибку сразу при назначении параметра, а не при выполнении вставки.

Например:

insert.Parameters[2].Value = Convert.ToDateTime(clickitem.ClickTime);

если это недопустимая дата и время, она завершится сбоем в Convert.ToDateTime, и вы заметите, что в своем журнале

person Steve    schedule 08.04.2013

трудный путь, но хороший дизайн. Я бы порекомендовал вам создать собственный пользовательский класс исключений, унаследовав базовый класс исключений.

Создайте конструктор, который принимает oledbcomndand в качестве входного параметра, и там вы можете попытаться зарегистрировать OldedbComamnd.CommandText, перебирая коллекцию параметров в цикле. как показано в примере ниже для SQLcommand (который более или менее совпадает с OLedbCommand)

или Easy Way - при возникновении исключения запишите в лог OLDEBCommand.ComamndText.

Ниже приведен пример, который я создал для StoredProcExecutionException для команды SQL. вы можете точно воспроизвести это для OleDbCommand. надеюсь это поможет

 public StoredProcExecutionException(string message, Exception innerException ,SqlCommand sqlCommand)
            : base(Convert.ToString(sqlCommand.CommandType ,CultureInfo.InvariantCulture)+" : "
                + Convert.ToString(sqlCommand.CommandText, CultureInfo.InvariantCulture)
                + "Failed. " + Convert.ToString(message, CultureInfo.InvariantCulture), innerException)
        {
            StringBuilder sb = new StringBuilder();

            foreach (SqlParameter param in sqlCommand.Parameters)
            {
                if (sb.Length > 0) sb.Append(",");
                sb.AppendFormat("{0}='{1}'", param.ParameterName, Convert.ToString(param.Value, CultureInfo.InvariantCulture));               
            }

            StringBuilder sbHeader = new StringBuilder();
            sbHeader.AppendLine(String.Format(CultureInfo.InvariantCulture,"{0} :{1} Failed. {2}", sqlCommand.CommandType, sqlCommand.CommandText, message));
            sbHeader.AppendFormat("Exec {0} ", sqlCommand.CommandText);

            sbHeader.Append(sb.ToString());

        }
person dekdev    schedule 08.04.2013
comment
@tomfanning: Хорошо, извините, исправил текст. Так сложно. Поднимите пользовательское исключение Итак, в этом случае переберите коллекцию параметров и запишите значения. я думаю, это должно сработать. - person dekdev; 08.04.2013