针对Oracle的C#参数化查询 - 严重且危险的错误!

Sha*_*ica 36 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;
Run Code Online (Sandbox Code Playgroud)

现在我执行以下代码:

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();
Run Code Online (Sandbox Code Playgroud)

哇!我收到ORA-01722错误 - "无效号码"!但是怎么了? Column1是数字,值为1,所以很好; Column2是一个字符串,Column3是一个可以为空的列,所以不应该造成任何麻烦......

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

当然,这导致我进入下一个明显的实验......让我们改变用于添加参数的代码块,如下所示:

p.Add("Foo", 1);
p.Add("Bar", "record 1");
p.Add("hahahahahahaha", null);
Run Code Online (Sandbox Code Playgroud)

你觉得那会有用吗?好吧猜猜 - 它确实如此!

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

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

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

sof*_*eda 44

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

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

  • 那么,我能说什么呢?你的答案是正确的 - 但我必须同意@Thomas这是一个愚蠢的"功能".更强:它是一个令人难以置信的愚蠢,误导和危险的功能,它应该被归类为一个bug.为什么**任何人**想要按顺序绑定他们的参数,而不是名字?并默认拥有这种行为?!这绝对是令人发指的! (7认同)
  • 究竟.这不是一个错误,只是一个愚蠢的"功能".所有提供者都按名称绑定参数,但不是ODP.NET,可能是因为Oracle不喜欢像其他人一样...我曾经在使用Oracle DB时对此感到厌恶,显然没有简单的方法默认情况下按名称绑定...有关此主题的更多信息,请参阅[此问题](http://stackoverflow.com/questions/1046632/binding-query-parameters-by-name-with-odp-net) (4认同)
  • @Shaul:我同意你的看法.但即使这样,臭虫也是Oracle而不是C#.您可以将此发布到oracle网站上的ODP.Net论坛,看看有人是否有理由回应.但在与Oracle合作之后,您会发现许多像这样的小宝石. (2认同)
  • **精细打印陷阱:**您期望某些东西(使用参数名称表示命名参数),但是当您运行它时,您会收到错误(如果您很幸运!).然后你会被告知:"如果你没有阅读,请注意细节,你自己的错!" 如果Oracle默认使用位置参数,则它们应仅在SQL中允许问号,并且仅按顺序或索引分配值!如果他们让用户指定参数名称并按名称分配值,那就是必须提供的命名参数的合约!我认为这个bug已经很老了. (2认同)

OMG*_*ies 5

您在第 2 列之前添加了第 3 列是错别字吗?

因为冒号语法表示绑定变量——名称与 PLSQL 中的 BIND 变量无关,它们按提交顺序填充。这意味着您将尝试将 column2 值设置为“记录 1”,这将解释无效数字错误...

您目前拥有:

p.Add("Column1", 1);
p.Add("Column3", null);
p.Add("Column2", "record 1");
Run Code Online (Sandbox Code Playgroud)

...看看这个改动是否能解决你的问题:

p.Add("Column1", 1);
p.Add("Column2", "record 1");
p.Add("Column3", null);
Run Code Online (Sandbox Code Playgroud)

让命名参数起作用?

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

  • @Shaul:在这种情况下,我和下一个幸灾乐祸的人一样内疚。如果只是因为下次是*我* :) (3认同)