针对Oracle的C#参数化查询 - 认真和&危险的虫子!

时间:2010-10-06 21:02:07

标签: c# oracle ora-01722

这绝对是咆哮。我不敢相信自己的眼睛,我不相信在我之前没有人会发现这个,如果它是C#中的一个真正的错误,所以我把它推出给其他开发者社区告诉我我做错了什么。我确定这个问题会让我说“DOH!”用我的手掌非常用力地拍打我的头 - 但无论如何......这就是......

为了测试,我创建了一个表Test_1,其脚本如下:

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是一个可以为空的列,因此不会造成任何麻烦......

现在请坐下来......这里的问题是Column3Column2按照添加到OracleParameterCollection的顺序进行转置。切换它们,并预先!它有效!

这当然引导我进入下一个明显的实验......让我们改变那些代码块来添加如下参数:

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

你认为那会奏效吗?猜猜是什么 - 确实

我坐在这里绝对惊呆了。我无法相信我所看到的,我也无法相信在我之前没有人发现过这种行为(除非我不知道如何正确使用Google)。

这不仅仅是一种烦恼 - 它非常危险。如果我转换了相同数据类型的两列,会发生什么?我甚至不会有错误 - 我只是将错误的数据插入到错误的列中,而且不是更明智的。

有没有人对解决方法有任何想法 - 除了小心不要以错误的顺序添加参数之外?

3 个答案:

答案 0 :(得分:44)

这不是一个错误,但在Oracle ODP.Net文档中明确提到过。在OracleCommand类中,参数按位置绑定为默认值。如果要按名称绑定,请明确设置属性cmd.BindByName = true;

参考Oracle文档。 http://download.oracle.com/docs/cd/E11882_01/win.112/e12249/OracleCommandClass.htm#i997666

答案 1 :(得分:4)

你是否在column2之前添加了column3?

因为冒号语法表示绑定变量 - 名称与PLSQL中的BIND变量无关,所以它们按提交顺序填充。这意味着您将尝试将column2值设置为“记录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);

使命名参数起作用?

我必须推荐具有更多C#经验的人来解释如何使命名参数正常工作。但我很高兴我们确认冒号似乎是在解释为Oracle BIND变量。

答案 2 :(得分:0)

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

//注意我已添加:到oracle数据客户端

识别的参数名称