使用 Null 从 C# 调用 Oracle 函数

Call an Oracle Function from C# with Nulls

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

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

  2. 我不知道是否需要在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 样东西:

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);
        }
    }