Query con parametri C # per Oracle: bug serio e pericoloso!

Questo è un assoluto ululato. Non posso credere ai miei occhi, e non posso credere che nessuno prima di me lo avrebbe scoperto se fosse un vero bug in C #, quindi lo spiego per il resto della comunità degli sviluppatori per dirmi cosa sto facendo male. Sono sicuro che questa domanda mi coinvolgerà dicendo “DOH!” e mi schiaffeggia molto forte con il palmo della mano – ma qui va, comunque …

Per motivi di prova, ho creato una tabella Test_1 , con script come segue:

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

Ora eseguo il seguente codice:

 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! Ottengo un errore ORA-01722 – “numero non valido”! Cosa c’è che non va, comunque? Column1 è numerica e ha un valore di 1, quindi va bene; Column2 è una stringa e Column3 è una colonna nullable, quindi non dovrebbe causare alcun problema …

Ora siediti per questo … il problema qui è che Column3 e Column2 sono trasposti nell’ordine in cui sono aggiunti a OracleParameterCollection . Passateli in giro, e presto! Funziona!

Questo, naturalmente, mi porta al prossimo ovvio esperimento … cambiamo quel blocco di codice per aggiungere parametri in questo modo:

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

Pensi che funzionerà? Beh, indovina un po ‘?

Sono seduto qui assolutamente stordito. Non posso credere a quello che sto vedendo, e allo stesso modo non posso credere che nessuno prima di me abbia scoperto questo comportamento (a meno che non sappia come usare Google correttamente).

Questo non è solo un fastidio: è seriamente pericoloso. Cosa sarebbe successo se avessi trasposto due colonne dello stesso tipo di dati? Non avrei nemmeno avuto un errore – avrei semplicemente inserito i dati sbagliati nelle colonne sbagliate, e non ne sono stato più saggio.

Qualcuno ha qualche idea per una soluzione alternativa, oltre a fare attenzione a non aggiungere parametri nell’ordine sbagliato?

Questo non è un bug ma è esplicitamente menzionato nella documentazione Oracle ODP.Net. In una class OracleCommand i parametri sono vincolati dalla posizione come predefinita. Se si desidera associare per nome, impostare la proprietà cmd.BindByName = true; in modo esplicito.

Riferimento alla documentazione Oracle. http://download.oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

È un errore di battitura in cui hai aggiunto column3 prima di column2?

Poiché la syntax del colon indica una variabile di binding: il nome non ha importanza per le variabili BIND in PLSQL, vengono popolate in ordine di invio. Il che significherebbe che si sta tentando di impostare il valore column2 come “record 1”, che spiegherebbe l’errore di numero non valido …

Attualmente hai:

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

… vedi se questa modifica risolve il tuo problema:

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

Come far funzionare i parametri con nome?

Devo rimandare a qualcuno con più esperienza in C # per spiegare come far funzionare i parametri con nome. Ma sono contento di aver confermato che il colon sembra interpretare come una variabile Oracle BIND.

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

// NOTA Ho aggiunto: ai nomi dei parametri che devono essere riconosciuti dal client di dati Oracle