我假设我在如何从 C# 运行 SQL 脚本方面做错了,但是在互联网上进行了大量搜索之后,我仍然不知道出了什么问题......
我在通过 C# 加载 Oracle 包和包体时遇到问题。当我通过脚本在 SQL*PLUS 中加载包时,随后对脚本中函数的调用正常工作。当我从 C# 调用它时,它也可以工作。但是,当我从 C# 加载相同的脚本时,脚本的运行似乎可以正常运行,但随后对包函数的调用(来自 C# 和 SQL*PLUS)失败并出现 PLS-00905 错误(“object ANON.MY_PKG is无效的”)。
SQL 脚本(“simple.sql”)的内容是:
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION my_function (
p_1 IN VARCHAR2
)RETURN VARCHAR2;
END my_pkg;
/
CREATE OR REPLACE PACKAGE BODY my_pkg IS
FUNCTION my_function (
p_1 IN VARCHAR2
) RETURN VARCHAR2 AS p_result VARCHAR2(2000);
BEGIN
RETURN p_1;
END my_function;
END my_pkg;
/
在 SQL*PLUS 中运行它可以正常工作...
SQL> SET SERVEROUTPUT ON
SQL> @"D:\_temp\simple.sql"
Package created.
Package body created.
SQL> EXEC DBMS_OUTPUT.PUT_LINE(my_pkg.my_function('hello'));
hello
PL/SQL procedure successfully completed.
然后在 C# 程序中调用该函数(作为 SQL*PLUS 中使用的同一 Oracle 用户)可以正常工作,直到在程序中重新运行 SQL 脚本。
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
OracleConnection conn = getConnection();
Debug.WriteLine("first call to my_function:");
callMyFunction(conn);
loadMyPackage(conn);
try
{
Debug.WriteLine("second call to my_function:");
callMyFunction(conn);
}
catch (Exception ex)
{
Debug.WriteLine("ex = " + ex.ToString());
}
}
private static OracleConnection getConnection()
{
string connStr = "redacted...";
OracleConnection conn = new OracleConnection();
conn.ConnectionString = connStr;
conn.Open();
return conn;
}
private static void loadMyPackage(OracleConnection conn)
{
OracleCommand command = new OracleCommand();
command.CommandType = CommandType.Text;
command.CommandText = File.ReadAllText(@"D:\_temp\simple.sql");
command.Connection = conn;
int response = command.ExecuteNonQuery();
Debug.WriteLine("response = " + response);
}
private static void callMyFunction(OracleConnection conn)
{
int RETURN_BUFFER_SIZE = 32767;
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "my_pkg.my_function";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("returnVal", OracleDbType.Varchar2, RETURN_BUFFER_SIZE);
cmd.Parameters["returnVal"].Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add("p_1", OracleDbType.Varchar2);
cmd.Parameters["p_1"].Value = "hello";
cmd.ExecuteNonQuery();
string result = cmd.Parameters[0].Value.ToString();
Debug.WriteLine("function result = " + result);
}
}
}
重新加载(“loadMyPackage”)后它会出错。具体来说:
first call to my_function:
function result = hello
response = -1
second call to my_function:
A first chance exception of type 'Oracle.ManagedDataAccess.Client.OracleException' occurred in Oracle.ManagedDataAccess.dll
ex = Oracle.ManagedDataAccess.Client.OracleException (0x00001996): ORA-06550: line 1, column 15:
PLS-00905: object ANON.MY_PKG is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
at OracleInternal.ServiceObjects.OracleCommandImpl.VerifyExecution(OracleConnectionImpl connectionImpl, Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, OracleException& exceptionForArrayBindDML, Boolean& hasMoreRowsInDB, Boolean bFirstIterationDone)
at OracleInternal.ServiceObjects.OracleCommandImpl.VerifyExecution(OracleConnectionImpl connectionImpl, Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, OracleException& exceptionForArrayBindDML, Boolean bFirstIterationDone)
at OracleInternal.ServiceObjects.OracleCommandImpl.ExecuteNonQuery(String commandText, OracleParameterCollection paramColl, CommandType commandType, OracleConnectionImpl connectionImpl, Int32 longFetchSize, Int64 clientInitialLOBFS, OracleDependencyImpl orclDependencyImpl, Int64[]& scnFromExecution, OracleParameterCollection& bindByPositionParamColl, Boolean& bBindParamPresent, OracleException& exceptionForArrayBindDML, Boolean isFromEF)
at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteNonQuery()
at TestApplication.Program.callMyFunction(OracleConnection conn) in c:\Users\ANON\Documents\Visual Studio 2013\Projects\myProject\TestApplication\Program.cs:line 73
at TestApplication.Program.Main(String[] args) in c:\Users\ANON\Documents\Visual Studio 2013\Projects\myProject\TestApplication\Program.cs:line 29
The thread 0x2838 has exited with code 259 (0x103).
The thread 0x2d9c has exited with code 259 (0x103).
The program '[11268] TestApplication.vshost.exe' has exited with code 0 (0x0).
如何让 C# 正确运行脚本?
更新: 根据反馈,我将 SQL 脚本分成两部分。第一部分(“simple_A.sql”)现在是:
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION my_function (
p_1 IN VARCHAR2
)RETURN VARCHAR2;
END my_pkg;
/
第二部分(“simple_B.sql”)是:
CREATE OR REPLACE PACKAGE BODY my_pkg IS -- body
FUNCTION my_function (
p_1 IN VARCHAR2
) RETURN VARCHAR2 AS p_result VARCHAR2(2000);
BEGIN
RETURN p_1;
END my_function;
END my_pkg;
/
在此之后,更改,我通过以下方式验证它在 SQL*PLUS 中仍然有效:
SQL> drop package my_pkg;
Package dropped.
SQL> @"D:\_temp\simple_A.sql"
Package created.
SQL> @"D:\_temp\simple_B.sql"
Package body created.
SQL> EXEC DBMS_OUTPUT.PUT_LINE(my_pkg.my_function('hello'));
hello
PL/SQL procedure successfully completed.
SQL>
然后我通过以下更改更新 C# 代码以使用这两个脚本:
private static void loadMyPackage(OracleConnection conn)
{
OracleCommand command_A = new OracleCommand();
command_A.CommandType = CommandType.Text;
command_A.CommandText = File.ReadAllText(@"D:\_temp\simple_A.sql");
command_A.Connection = conn;
int response = command_A.ExecuteNonQuery();
Debug.WriteLine("response A = " + response);
OracleCommand command_B = new OracleCommand();
command_B.CommandType = CommandType.Text;
command_B.CommandText = File.ReadAllText(@"D:\_temp\simple_B.sql");
command_B.Connection = conn;
response = command_B.ExecuteNonQuery();
Debug.WriteLine("response B = " + response);
}
但是,我仍然遇到同样的错误。具体来说,现在的输出如下:
first call to my_function:
function result = hello
response A = -1
response B = -1
second call to my_function:
A first chance exception of type 'Oracle.ManagedDataAccess.Client.OracleException' occurred in Oracle.ManagedDataAccess.dll
ex = Oracle.ManagedDataAccess.Client.OracleException (0x00001996): ORA-06550: line 1, column 15:
PLS-00905: object ANON.MY_PKG is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
at OracleInternal.ServiceObjects.OracleCommandImpl.VerifyExecution(OracleConnectionImpl connectionImpl, Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, OracleException& exceptionForArrayBindDML, Boolean& hasMoreRowsInDB, Boolean bFirstIterationDone)
at OracleInternal.ServiceObjects.OracleCommandImpl.VerifyExecution(OracleConnectionImpl connectionImpl, Int32& cursorId, Boolean bThrowArrayBindRelatedErrors, OracleException& exceptionForArrayBindDML, Boolean bFirstIterationDone)
at OracleInternal.ServiceObjects.OracleCommandImpl.ExecuteNonQuery(String commandText, OracleParameterCollection paramColl, CommandType commandType, OracleConnectionImpl connectionImpl, Int32 longFetchSize, Int64 clientInitialLOBFS, OracleDependencyImpl orclDependencyImpl, Int64[]& scnFromExecution, OracleParameterCollection& bindByPositionParamColl, Boolean& bBindParamPresent, OracleException& exceptionForArrayBindDML, Boolean isFromEF)
at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteNonQuery()
...
The thread 0x4f0 has exited with code 259 (0x103).
The thread 0x3310 has exited with code 259 (0x103).
The program '[10728] TestApplication.vshost.exe' has exited with code 0 (0x0).
更新 #2: 根据贾斯汀的评论,我将我的 sql 脚本拆分为两个单独的文件,并从中删除了“/”字符。他们现在是:
简单_A.sql:
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION my_function (
p_1 IN VARCHAR2
)RETURN VARCHAR2;
END my_pkg;
simple_B.sql
CREATE OR REPLACE PACKAGE BODY my_pkg IS -- body
FUNCTION my_function (
p_1 IN VARCHAR2
) RETURN VARCHAR2 AS p_result VARCHAR2(2000);
BEGIN
RETURN p_1;
END my_function;
END my_pkg;
通过这些更改以及第一次更新中的更改,代码可以正常工作。
在让代码正常工作后,我根据 Justin 的另一条评论添加了一个额外的位,这是一种识别错误原因的方法。具体来说,我添加了一个 printErrors 函数,并在我的 C# 代码中添加了对它的调用。对代码的添加和修改如下:
private static void loadMyPackage(OracleConnection conn)
{
OracleCommand command_A = new OracleCommand();
command_A.CommandType = CommandType.Text;
command_A.CommandText = File.ReadAllText(@"D:\_temp\simple_A.sql");
command_A.Connection = conn;
int response = command_A.ExecuteNonQuery();
printErrors(conn);
Debug.WriteLine("response A = " + response);
OracleCommand command_B = new OracleCommand();
command_B.CommandType = CommandType.Text;
command_B.CommandText = File.ReadAllText(@"D:\_temp\simple_B.sql");
command_B.Connection = conn;
response = command_B.ExecuteNonQuery();
printErrors(conn);
Debug.WriteLine("response B = " + response);
}
private static void printErrors(OracleConnection conn)
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT name, text FROM user_errors";
cmd.CommandType = CommandType.Text;
OracleDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Debug.WriteLine("user_error: " + dr.GetString(0) + ": " + dr.GetString(1) );
}
}
添加 printErrors 代码后,错误的原因就很容易看到了。第一次更新的错误 SQL 版本会产生以下输出:
...
first call to my_function:
function result = hello
user_error: MY_PKG: PLS-00103: Encountered the symbol "/" The symbol "/" was ignored.
response A = -1
...
[注意:上述错误信息来自最终代码,当给定有错误的 sql 脚本时。sql 脚本的最终版本没有错误。]
因此,这段代码不仅开始工作,而且现在能够在失败的情况下提供描述性错误信息。