【问题标题】:Call an Oracle Function from C# with Nulls使用 Null 从 C# 调用 Oracle 函数
【发布时间】:2023-03-10 06:40:01
【问题描述】:

我正在尝试从我们的 C# 应用程序调用 Oracle 函数,但我得到以下错误。我认为我有两个问题:

  1. 我想调用这个函数,但是有些参数在C#端可以为null,所以不知道怎么处理。

  2. 不知道是否需要在OracleParameter对象上加上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 将删除带有null 的参数,因为那里有.Value。这是真的?我应该传入什么来表明该参数没有值?

【问题讨论】:

  • 我对这个主题非常模糊的记忆说你通过了System.DBNull.Value
  • @vc74 从在线阅读看来,ExecuteNonQuery 返回更新数据库时影响的行数,而ExecuteScalar 用于只需要一个值,例如COUNT(*)
  • 关于 ExecuteScalar 与 ExecuteNonQuery 参见 this answer

标签: c# oracle12c data-access-layer odp.net


【解决方案1】:

你至少需要 4 样东西:

您以与存储过程相同的方式执行该函数。使用 ParameterDirection.ReturnValue 参数获取函数返回的结果。

  • 在第三个方面,请使用DbNull.Value,因为它专门设计为数据库中represent null values 的占位符,而null 仅对.NET 有意义。 (好吧,null 可能没问题,因为 Oracle 驱动程序可能足够聪明,可以处理它;DbNull.Value 是一个好习惯,因为你很明确)。你可以这样做

new OracleParameter("typCds", OracleDbType.Varchar2, typCds ?? (object)DbNull.Value, ParameterDirection.Input));

  • 最后,您在参数上按名称绑定,但名称与参数名称不匹配。完全匹配名称或按位置绑定。

至于具体错误,返回值为“参数”,没有正确绑定参数。 Oracle 想要 13 个参数,而你实际上没有给它。

【讨论】:

  • 我已经尝试了所有提到的三个。使用ExecuteNonQuery,在ReturnValue参数中添加,还使用??运算符检查空值,但我仍然得到同样的错误。 PLS-00306: wrong number or types of arguments in call to 'FUNC_SEARCHROWCOUNT'
  • 我想我可能会看到它,如果正确我会更新我的答案。您已在参数上按名称绑定,但名称与参数名称不匹配。请尝试完全匹配名称或按位置绑定。它可能默认所有参数(计数为零,因为没有绑定)。
  • 就是这样!我一直认为BindByName 只用在应用程序的C# 端。我不知道它也适用于你的电话。删除 BindByName 修复它。
【解决方案2】:

您的代码似乎有几个问题。 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 说“嘿,按名称绑定集合中的参数,而不是使用位置”。在这种情况下,它们必须完全匹配。

你的C#方法返回值是int(System.Int32),PlSql封装函数的返回值是NUMBER。如果数字没有指定比例,ODP.Net 似乎在 C# 中返回十进制。当 ODP.Net 尝试在内部将 oracle 数字类型转换为短 (Int16) 时,您可能会遇到转换/无效转换异常。当返回的计数大于 short.MaxValue 时,可能会出现超出范围的异常。尝试在创建返回值参数时将 Int32 指定为返回值。

OracleCommand 实现 IDisposable 接口。请确保在不再需要时释放您的命令,因为对象中 IDisposable 接口的实现表明该对象创建/使用了一些资源(托管或非托管)并且必须在操作完成时释放它们。最短的方法是使用 C#的“using”关键字,确保在代码执行离开块时调用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);
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    相关资源
    最近更新 更多