C # consultas parametrizadas para Oracle: ¡error grave y peligroso!

Este es un aullador absoluto. No puedo creer lo que pienso, y no puedo creer que nadie antes que yo hubiera descubierto esto si se tratara de un error genuino en C #, así que lo estoy presentando para que el rest de la comunidad de desarrolladores me diga lo que estoy haciendo mal. Estoy seguro de que esta pregunta me implicará diciendo “¡DOH!” y golpeándome la cabeza con la palma de la mano, pero aquí va, de todos modos …

Por el bien de las pruebas, he creado una tabla Test_1 , con script de la siguiente manera:

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

Ahora ejecuto el siguiente código:

 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(); 

Whoa! ¡Recibo un error ORA-01722 – “número inválido”! ¿Qué está mal, sin embargo? Column1 es numérico y tiene un valor de 1, así que está bien; Column2 es una cadena, y Column3 es una columna que Column3 , por lo que no debería causar ningún problema …

Ahora siéntese por este … el problema aquí es que Column3 y Column2 se transponen en el orden en que se agregan a OracleParameterCollection . ¡Cambielos, y listo! ¡Funciona!

Esto, por supuesto, me lleva al siguiente experimento obvio … vamos a cambiar ese bloque de código para agregar parámetros como ese:

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

¿Crees que eso funcionará? Bueno, adivina qué, ¡ !

Estoy sentado aquí completamente aturdido. No puedo creer lo que estoy viendo, y tampoco puedo creer que nadie antes que yo haya descubierto este comportamiento (a menos que no sepa cómo usar Google correctamente).

Esto no es solo una molestia, es muy peligroso. ¿Qué hubiera pasado si hubiera transpuesto dos columnas del mismo tipo de datos? Ni siquiera habría recibido un error: simplemente habría insertado los datos incorrectos en las columnas incorrectas, y no he sido más prudente.

¿Alguien tiene alguna idea para una solución alternativa, aparte de tener cuidado de no agregar parámetros en el orden incorrecto?

Esto no es un error, sino que se menciona explícitamente en la documentación de Oracle ODP.Net. En una clase OracleCommand, los parámetros están vinculados por posición como predeterminados. Si desea enlazar por nombre, establezca la propiedad cmd.BindByName = true; explícitamente.

Referencia a la documentación de Oracle. http://download.oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

¿Es un error tipográfico que tienes column3 agregado antes de column2?

Debido a que la syntax de dos puntos significa una variable de vinculación, el nombre no importa para las variables BIND en PLSQL, se completan por orden de envío. Lo que significaría que intentaría establecer el valor de la columna 2 como “registro 1”, lo que explicaría el error de número inválido …

Tu Actualmente tienes:

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

… mira si esta alteración soluciona tu problema:

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

Cómo hacer que los parámetros nombrados funcionen?

Debo ceder ante alguien con más experiencia en C # para explicar cómo funcionan los parámetros nombrados. Pero me alegro de que hayamos confirmado que el colon parece estar interpretando como una variable BIND de Oracle.

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

// NOTA que he agregado: a los nombres de los parámetros para ser reconocidos por el cliente de datos de Oracle