С# параметризованные запросы для Oracle - серьезная и опасная ошибка!

Это абсолютный воющий. Я не могу поверить своим собственным глазам, и я не могу поверить, что никто до меня не обнаружил бы это, если бы это была настоящая ошибка на С#, поэтому я передаю ее остальному сообществу разработчиков, чтобы сказать мне, что я делаю неправильно. Я уверен, что этот вопрос заставит меня сказать "DOH!" и сильно ударился головой о ладонь моей руки - но здесь все равно...

Для тестирования я создал таблицу Test_1, с script следующим образом:

CREATE TABLE TEST_1 (
  COLUMN1 NUMBER(12) NOT NULL,
  COLUMN2 VARCHAR2(20),
  COLUMN3 NUMBER(12))
TABLESPACE USERS
STORAGE (
  INITIAL 64K
  MAXEXTENTS UNLIMITED
)
LOGGING;

Теперь я выполняю следующий код:

var conn = new OracleConnection("connectionblahblah");
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = 
  "insert into Test_1(Column1, Column2, Column3) " +
  "values(:Column1, :Column2, :Column3)";
var p = cmd.Parameters;
p.Add("Column1", 1);
p.Add("Column3", null);
p.Add("Column2", "record 1");
cmd.ExecuteNonQuery();

Вау! Я получаю ошибку ORA-01722 - "недопустимое число"! Что случилось, правда? Column1 является числовым и имеет значение 1, так что штраф; Column2 - это строка, а Column3 - столбец с нулевым значением, поэтому это не должно вызывать никаких проблем...

Теперь садитесь за это... проблема в том, что Column3 и Column2 транспонируются в том порядке, в котором они добавлены в OracleParameterCollection. Переключайте их, и престо! Он работает!

Это, конечно, приводит меня к следующему очевидному эксперименту... пусть изменит этот блок кода для добавления таких параметров:

p.Add("Foo", 1);
p.Add("Bar", "record 1");
p.Add("hahahahahahaha", null);

Ты думаешь, что сработает? Угадайте, что - он делает!

Я сижу здесь, совершенно ошеломленный. Я не могу поверить в то, что вижу, и я тоже не могу поверить, что никто из меня не обнаружил этого поведения (если я не знаю, как правильно использовать Google).

Это не просто раздражение - это серьезно опасно. Что произошло бы, если бы я переместил два столбца одного и того же типа данных? Я бы даже не получил ошибку - я бы просто ввел неправильные данные в неправильные столбцы и не стал более мудрым.

Есть ли у кого-нибудь идеи для обходного пути - кроме как быть осторожным, чтобы не добавлять параметры в неправильном порядке?

Ответ 1

Это не ошибка, но явно упомянутая в документации Oracle ODP.Net. В классе OracleCommand параметры привязаны по умолчанию по умолчанию. Если вы хотите связать по имени, тогда явно установите свойство cmd.BindByName = true;.

Ссылка на документацию Oracle. http://download.oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

Ответ 2

Это опечатка, которую вы добавили в колонку3 перед столбцом2?

Поскольку синтаксис двоеточия означает переменную привязки - имя не имеет значения для переменных BIND в PLSQL, они заполняются в порядке отправки. Это означало бы, что вы пытаетесь установить значение столбца как "запись 1", что объясняет неверную ошибку номера...

В настоящее время у вас есть:

p.Add("Column1", 1);
p.Add("Column3", null);
p.Add("Column2", "record 1");

... проверьте, исправляет ли это изменение вашу проблему:

p.Add("Column1", 1);
p.Add("Column2", "record 1");
p.Add("Column3", null);

Получение именованных параметров для работы?

Мне приходится откладывать на кого-то более опытный С#, чтобы объяснить, как заставить работать именованные параметры. Но я рад, что мы подтвердили, что двоеточие интерпретируется как переменная Oracle BIND.

Ответ 3

p.Add(":Column1", 1);
p.Add(":Column2", "record 1");
p.Add(":Column3", null);

//ПРИМЕЧАНИЕ. Я добавил: к именам параметров, которые будут распознаны клиентом данных oracle