С# параметризованные запросы для 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