tags:

views:

101

answers:

2

This is an absolute howler. I cannot believe my own eyes, and I cannot believe nobody before me would have discovered this if it was a genuine bug in C#, so I'm putting it out for the rest of the developer community to tell me what I am doing wrong. I'm sure this question is going to involve me saying "DOH!" and smacking my head very hard with the palm of my hand - but here goes, anyway...

For the sake of testing, I have created a table Test_1, with script as follows:

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

Now I execute the following code:

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! I get an ORA-01722 error - "invalid number"! What's wrong, though? Column1 is numeric, and has a value of 1, so that's fine; Column2 is a string, and Column3 is a nullable column, so that shouldn't cause any trouble...

Now sit down for this one... the problem here is that Column3 and Column2 are transposed in the order in which they are added to the OracleParameterCollection. Switch them around, and presto! It works!

This, of course, leads me to the next obvious experiment... let's change that block of code for adding parameters like so:

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

You think that'll work? Well guess what - it does!

I am sitting here absolutely stunned. I cannot believe what I am seeing, and I likewise cannot believe that nobody before me has discovered this behavior (unless I don't know how to use Google properly).

This is not just an annoyance - it is seriously dangerous. What would have happened if I'd transposed two columns of the same data type? I wouldn't have even got an error - I would have simply inserted the wrong data into the wrong columns, and been none the wiser.

Does anyone have any ideas for a workaround - other than just being careful not to add parameters in the wrong order?

+2  A: 

Is that a typo that you have column3 being added before column2?

Because the colon syntax signifies a bind variable--name doesn't matter to BIND variables in PLSQL, they're populated in order of submission. Which would mean you'd be attempting to set column2 value as "record 1", which would explain the invalid number error...

You currently have:

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

...see if this alteration fixes your issue:

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

Getting Named Parameters to Work?

I have to defer to someone with more C# experience to explain how to get named parameters working. But I'm glad we confirmed that the colon appears to be interpreting as an Oracle BIND variable.

OMG Ponies
So then what is the correct way of passing named parameters instead of ordered?
Shaul
@Shaul: See update
OMG Ponies
@OMG: You haven't really answered my question; you've just restated my discovery that the parameter names are ignored, and it only uses the order in which they're added. I want to be able to add parameters by **name**, and **not care** about what order I add them in. How do I do that?
Shaul
@OMG: In any case, this is what I call a "stumbling block in the public domain" - why bother having a parameter name in the interface if you're just going to ignore it anyway? All it does is confuse the programmers and leads them to making possibly very severe mistakes, such as transposing values in fields. What if it hadn't thrown an exception?
Shaul
@Shaul: Sorry about that. [Based on the documentation, the colon should work but the examples don't reinforce that](http://msdn.microsoft.com/en-us/library/ebxy9a8b%28VS.71%29.aspx). It looks like you should change the ":" to "@".for named parameters to work. Odd, considering @ is TSQL notation.
OMG Ponies
@Shaul: Regarding "pit in the public domain" -- I agree, it's an odd choice but sadly the likelihood of changing it is small for sake of backwards compatibility :/
OMG Ponies
@Shaul: Just to confirm -- the example did work if parameters were provided in order rather than by name?
OMG Ponies
@OMG: The "@" syntax there refers to MS SQL, not Oracle. I can't see any other syntax for named parameters in Oracle other than the ":" syntax. (And just to be sure, I tried the "@" syntax - it doesn't work.)
Shaul
@OMG: Yes, the example works if you provide the parameters in the same order in which they appear in the query. (Actually I mistranslated the expression - should be "Pit in the public domain".)
Shaul
@Shaul: Thx, that makes sense about ":" vs "@". Sorry, I don't have the C# background to help you further. Cool, so it is interpreting the variables as BIND variables...
OMG Ponies
@OMG: **"Cool"!!?** I've got other words to describe this that don't bear publishing...! ;)
Shaul
@Shaul: I'm as guilty as the next person for schadenfreude in situations like these. If only because it'll be *me* next time :)
OMG Ponies
+7  A: 

This is not a bug but explicitly mentioned in Oracle ODP.Net documentation. In a OracleCommand class the parameters are bound by position as default. If you want to bind by name then set the property cmd.BindByName = true; explicitly.

Reference to Oracle documentation. http://download.oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

Pratik
Exactly. That's not a bug, just a stupid "feature". All providers bind parameters by name, but not ODP.NET, perhaps because Oracle doesn't like to do like everyone else... I used to bitch at this when I was working with an Oracle DB, there is apparently no simple way to bind by name by default... See [this question](http://stackoverflow.com/questions/1046632/binding-query-parameters-by-name-with-odp-net) for more on the subject
Thomas Levesque
@Pratik: Well, what can I say? Your answer is correct - but I have to concur with @Thomas that this is a stupid "feature". Stronger: it's a feature so mindbogglingly stupid, misleading and dangerous that it should be classified as a bug. Why on earth would **anyone** want to bind their parameters in order, rather than by name? And to have that behavior by default?! This is absolutely outrageous!
Shaul
@Shaul: I agree with you. But even then the bug is with Oracle not C#. You can post this to ODP.Net forum at oracle's website and see if someone responds with a reason. But after working with Oracle you will find many little gems like this.
Pratik