使用 Null 从 C# 调用 Oracle 函数
Call an Oracle Function from C# with Nulls
我正在尝试从我们的 C# 应用程序调用 Oracle 函数,但我遇到了以下错误。我想我有两个问题:
我想调用这个函数,但是C#端有些参数可以为null,不知道怎么处理。
我不知道是否需要在OracleParameter
对象上将return值添加到带有ParameterDirection.ReturnValue
的参数中。
这就是我正在尝试的:
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
OracleCommand cmd = null;
try
{
StringBuilder sql = new StringBuilder();
sql.Append(" pack_SomePack.func_SearchRowCount");
cmd = new OracleCommand(sql.ToString(), this.Connection);
cmd.CommandType = CommandType.StoredProcedure;
// Don't know if I should add this guy
// cmd.Parameters.Add(new OracleParameter("RowCount", OracleDbType.Int16, ParameterDirection.ReturnValue));
cmd.Parameters.Add(new OracleParameter("FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("grpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("catCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("typCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("memNbr", OracleDbType.Long, memNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("SubNbr", OracleDbType.Long, SubNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("searchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("statCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("showUncategorized", OracleDbType.Char, showUncategorized? "Y" : "N", ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("debugYN", OracleDbType.Varchar2, debugYN, ParameterDirection.Input));
cmd.BindByName = true;
int activityRowCount = Convert.ToInt16(cmd.ExecuteScalar()); // Error here
return activityRowCount;
}
我的函数执行以下操作:
FUNCTION func_SearchRowCount
(
in_FromDate IN DATE,
in_ThruDate IN DATE,
in_GrpCds IN VARCHAR2,
in_CatCds IN VARCHAR2,
in_TypCds IN VARCHAR2,
in_MemNbr IN Actv.PersNbr%TYPE,
in_SubNbr IN Actv.SubNbr%TYPE,
in_SearchBy IN VARCHAR2,
in_dispActivity IN VARCHAR2,
in_StatCds IN Ams.StatCd%TYPE,
in_UncategorizedYN IN CHAR,
in_DebugYN IN CHAR
) RETURN NUMBER AS
lvnCount NUMBER;
lvsSqlStr VARCHAR2(2000);
BEGIN
lvsSqlStr := 'SELECT COUNT(*) FROM SomeTable WHERE (Include a bunch of clauses..)';
BEGIN
EXECUTE IMMEDIATE lvsSqlStr
INTO lvnCount
USING (All the parameters);
END;
RETURN lvnCount;
END func_SearchRowCount;
当 运行 以上内容时,出现以下错误。
PLS-00306: wrong number or types of arguments in call to 'FUNC_SEARCHROWCOUNT'
所有变量都绑定了正确的数量,尽管我在某处读到 ODP.NET 会像 .Value
一样删除带有 null
的参数。这是真的?我应该传入什么来表明该参数没有值?
你至少需要 4 样东西:
- 前两个:调用
ExecuteNonQuery
而不是 ExecuteScalar
中讨论的 MSDN thread and create the return value parameter. About a third of the way down on Accessing Oracle 9i Stored Procedures 它显示了这段代码并说
You execute the function in the same way as a stored procedure. Use a ParameterDirection.ReturnValue parameter to get the result returned by the function.
- 第三,请务必使用
DbNull.Value
,因为它专门设计为数据库中 represent null values 的占位符,而 null
仅对 .NET 有意义。 (好吧,null
可能没问题,因为 Oracle 驱动程序可能足够聪明来处理它;DbNull.Value
是一个好习惯,因为你是明确的)。你可以这样做
new OracleParameter("typCds", OracleDbType.Varchar2, typCds ?? (object)DbNull.Value, ParameterDirection.Input));
- 最后,您在参数上按名称绑定,但名称与参数名称不匹配。完全匹配名称或按位置绑定。
具体错误,return值为"an argument",没有正确绑定参数。 Oracle 想要 13 个参数,而您实际上给了它 none.
您的代码似乎有几个问题。
Oracle 类型 LONG 与 C# 中的不同,Oracle DB 中的 LONG 允许您存储最大 2GB 大小的字符数据。在 C# 中,它是使用 64 位的数字类型。由于您提交的代码没有解释您的包函数中的参数 in_MemNbr、in_SubNbr 和 in_StatCds 是什么类型的数据,我只能猜测,它是什么取决于您对您的 C# 方法中的参数列表。
"new OracleParameter("")" 语句中的 C# 参数名称与函数参数不完全匹配。在 Pl/Sql 中,您添加了一个 "in_" 前缀,但在 C# 代码中将其删除。使用 "cmd.BindByName = true;" 你说 ODP.Net "Hey, bind the parameters in the collection by name rather than using position"。在这种情况下,它们必须完全匹配。
你的C#return方法的值是int(System.Int32),PlSql包函数的return值是NUMBER。 ODP.Net 在 C# 中似乎是 return 小数,以防数字没有指定比例。当 ODP.Net 试图在内部将 oracle 数字类型转换为 short (Int16) 时,您可能 运行 变成了 conversion/invalidcast 异常。当 returned 计数大于 short.MaxValue 时,您可能会遇到超出范围的异常。尝试在 return 值参数创建中将 Int32 指定为 return 值。
OracleCommand 实现了 IDisposable 接口。请确保在不再需要时释放您的命令,因为对象中 IDisposable 接口的实现向您显示对象 creates/uses 一些资源(托管或非托管)并且必须在操作完成时释放它们。最短的方法是使用
"using" C# 的关键字,它确保在代码执行离开块时调用 cmd.Dispose(),无论是否发生异常或块是否成功结束。
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
using (var cmd = new OracleCommand("pack_SomePack.func_SearchRowCount", this.Connection))
using (var result = new OracleParameter("result", OracleDbType.Int32, ParameterDirection.ReturnValue))
using (var fromDateParam = new OracleParameter("in_FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input))
using (var thruDateParam = new OracleParameter("in_ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input))
using (var grpCdsParam = new OracleParameter("in_GrpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input))
using (var catCdsParam = new OracleParameter("in_CatCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input))
using (var typCdsParam = new OracleParameter("in_TypCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input))
using (var memNbrParam = new OracleParameter("in_MemNbr", OracleDbType.Int64, memNbr, ParameterDirection.Input))
using (var subNbrParam = new OracleParameter("in_SubNbr", OracleDbType.Int64, SubNbr, ParameterDirection.Input))
using (var searchByParam = new OracleParameter("in_SearchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input))
using (var dispActivityParam = new OracleParameter("in_dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input))
using (var statCdsParam = new OracleParameter("in_StatCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input))
using (var uncategorizedYnParam = new OracleParameter("in_UncategorizedYN", OracleDbType.Char, showUncategorized ? "Y" : "N", ParameterDirection.Input))
using (var debugYnParam = new OracleParameter("in_DebugYN", OracleDbType.Char, debugYN, ParameterDirection.Input))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(result);
cmd.Parameters.Add(fromDateParam);
cmd.Parameters.Add(thruDateParam);
cmd.Parameters.Add(grpCdsParam);
cmd.Parameters.Add(catCdsParam);
cmd.Parameters.Add(typCdsParam);
cmd.Parameters.Add(memNbrParam);
cmd.Parameters.Add(subNbrParam);
cmd.Parameters.Add(searchByParam);
cmd.Parameters.Add(dispActivityParam);
cmd.Parameters.Add(statCdsParam);
cmd.Parameters.Add(uncategorizedYnParam);
cmd.Parameters.Add(debugYnParam);
cmd.BindByName = true;
cmd.ExecuteNonQuery();
return Convert.ToInt32(result.Value);
}
}
我正在尝试从我们的 C# 应用程序调用 Oracle 函数,但我遇到了以下错误。我想我有两个问题:
我想调用这个函数,但是C#端有些参数可以为null,不知道怎么处理。
我不知道是否需要在
OracleParameter
对象上将return值添加到带有ParameterDirection.ReturnValue
的参数中。
这就是我正在尝试的:
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
OracleCommand cmd = null;
try
{
StringBuilder sql = new StringBuilder();
sql.Append(" pack_SomePack.func_SearchRowCount");
cmd = new OracleCommand(sql.ToString(), this.Connection);
cmd.CommandType = CommandType.StoredProcedure;
// Don't know if I should add this guy
// cmd.Parameters.Add(new OracleParameter("RowCount", OracleDbType.Int16, ParameterDirection.ReturnValue));
cmd.Parameters.Add(new OracleParameter("FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("grpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("catCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("typCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("memNbr", OracleDbType.Long, memNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("SubNbr", OracleDbType.Long, SubNbr, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("searchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("statCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("showUncategorized", OracleDbType.Char, showUncategorized? "Y" : "N", ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("debugYN", OracleDbType.Varchar2, debugYN, ParameterDirection.Input));
cmd.BindByName = true;
int activityRowCount = Convert.ToInt16(cmd.ExecuteScalar()); // Error here
return activityRowCount;
}
我的函数执行以下操作:
FUNCTION func_SearchRowCount
(
in_FromDate IN DATE,
in_ThruDate IN DATE,
in_GrpCds IN VARCHAR2,
in_CatCds IN VARCHAR2,
in_TypCds IN VARCHAR2,
in_MemNbr IN Actv.PersNbr%TYPE,
in_SubNbr IN Actv.SubNbr%TYPE,
in_SearchBy IN VARCHAR2,
in_dispActivity IN VARCHAR2,
in_StatCds IN Ams.StatCd%TYPE,
in_UncategorizedYN IN CHAR,
in_DebugYN IN CHAR
) RETURN NUMBER AS
lvnCount NUMBER;
lvsSqlStr VARCHAR2(2000);
BEGIN
lvsSqlStr := 'SELECT COUNT(*) FROM SomeTable WHERE (Include a bunch of clauses..)';
BEGIN
EXECUTE IMMEDIATE lvsSqlStr
INTO lvnCount
USING (All the parameters);
END;
RETURN lvnCount;
END func_SearchRowCount;
当 运行 以上内容时,出现以下错误。
PLS-00306: wrong number or types of arguments in call to 'FUNC_SEARCHROWCOUNT'
所有变量都绑定了正确的数量,尽管我在某处读到 ODP.NET 会像 .Value
一样删除带有 null
的参数。这是真的?我应该传入什么来表明该参数没有值?
你至少需要 4 样东西:
- 前两个:调用
ExecuteNonQuery
而不是ExecuteScalar
中讨论的 MSDN thread and create the return value parameter. About a third of the way down on Accessing Oracle 9i Stored Procedures 它显示了这段代码并说
You execute the function in the same way as a stored procedure. Use a ParameterDirection.ReturnValue parameter to get the result returned by the function.
- 第三,请务必使用
DbNull.Value
,因为它专门设计为数据库中 represent null values 的占位符,而null
仅对 .NET 有意义。 (好吧,null
可能没问题,因为 Oracle 驱动程序可能足够聪明来处理它;DbNull.Value
是一个好习惯,因为你是明确的)。你可以这样做
new OracleParameter("typCds", OracleDbType.Varchar2, typCds ?? (object)DbNull.Value, ParameterDirection.Input));
- 最后,您在参数上按名称绑定,但名称与参数名称不匹配。完全匹配名称或按位置绑定。
具体错误,return值为"an argument",没有正确绑定参数。 Oracle 想要 13 个参数,而您实际上给了它 none.
您的代码似乎有几个问题。 Oracle 类型 LONG 与 C# 中的不同,Oracle DB 中的 LONG 允许您存储最大 2GB 大小的字符数据。在 C# 中,它是使用 64 位的数字类型。由于您提交的代码没有解释您的包函数中的参数 in_MemNbr、in_SubNbr 和 in_StatCds 是什么类型的数据,我只能猜测,它是什么取决于您对您的 C# 方法中的参数列表。
"new OracleParameter("")" 语句中的 C# 参数名称与函数参数不完全匹配。在 Pl/Sql 中,您添加了一个 "in_" 前缀,但在 C# 代码中将其删除。使用 "cmd.BindByName = true;" 你说 ODP.Net "Hey, bind the parameters in the collection by name rather than using position"。在这种情况下,它们必须完全匹配。
你的C#return方法的值是int(System.Int32),PlSql包函数的return值是NUMBER。 ODP.Net 在 C# 中似乎是 return 小数,以防数字没有指定比例。当 ODP.Net 试图在内部将 oracle 数字类型转换为 short (Int16) 时,您可能 运行 变成了 conversion/invalidcast 异常。当 returned 计数大于 short.MaxValue 时,您可能会遇到超出范围的异常。尝试在 return 值参数创建中将 Int32 指定为 return 值。
OracleCommand 实现了 IDisposable 接口。请确保在不再需要时释放您的命令,因为对象中 IDisposable 接口的实现向您显示对象 creates/uses 一些资源(托管或非托管)并且必须在操作完成时释放它们。最短的方法是使用 "using" C# 的关键字,它确保在代码执行离开块时调用 cmd.Dispose(),无论是否发生异常或块是否成功结束。
public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, string catCds, string typCds, long? memNbr, long? subNbr, string searchBy, string dispActivity, string statCds, bool showUncategorized, string debugYN)
{
using (var cmd = new OracleCommand("pack_SomePack.func_SearchRowCount", this.Connection))
using (var result = new OracleParameter("result", OracleDbType.Int32, ParameterDirection.ReturnValue))
using (var fromDateParam = new OracleParameter("in_FromDate", OracleDbType.Date, fromDate, ParameterDirection.Input))
using (var thruDateParam = new OracleParameter("in_ThruDate", OracleDbType.Date, thruDate, ParameterDirection.Input))
using (var grpCdsParam = new OracleParameter("in_GrpCds", OracleDbType.Varchar2, grpCds, ParameterDirection.Input))
using (var catCdsParam = new OracleParameter("in_CatCds", OracleDbType.Varchar2, catCds, ParameterDirection.Input))
using (var typCdsParam = new OracleParameter("in_TypCds", OracleDbType.Varchar2, typCds, ParameterDirection.Input))
using (var memNbrParam = new OracleParameter("in_MemNbr", OracleDbType.Int64, memNbr, ParameterDirection.Input))
using (var subNbrParam = new OracleParameter("in_SubNbr", OracleDbType.Int64, SubNbr, ParameterDirection.Input))
using (var searchByParam = new OracleParameter("in_SearchBy", OracleDbType.Varchar2, searchBy, ParameterDirection.Input))
using (var dispActivityParam = new OracleParameter("in_dispActivity", OracleDbType.Varchar2, dispActivity, ParameterDirection.Input))
using (var statCdsParam = new OracleParameter("in_StatCds", OracleDbType.Varchar2, statCds, ParameterDirection.Input))
using (var uncategorizedYnParam = new OracleParameter("in_UncategorizedYN", OracleDbType.Char, showUncategorized ? "Y" : "N", ParameterDirection.Input))
using (var debugYnParam = new OracleParameter("in_DebugYN", OracleDbType.Char, debugYN, ParameterDirection.Input))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(result);
cmd.Parameters.Add(fromDateParam);
cmd.Parameters.Add(thruDateParam);
cmd.Parameters.Add(grpCdsParam);
cmd.Parameters.Add(catCdsParam);
cmd.Parameters.Add(typCdsParam);
cmd.Parameters.Add(memNbrParam);
cmd.Parameters.Add(subNbrParam);
cmd.Parameters.Add(searchByParam);
cmd.Parameters.Add(dispActivityParam);
cmd.Parameters.Add(statCdsParam);
cmd.Parameters.Add(uncategorizedYnParam);
cmd.Parameters.Add(debugYnParam);
cmd.BindByName = true;
cmd.ExecuteNonQuery();
return Convert.ToInt32(result.Value);
}
}