从C#调用带有null的Oracle函数

从C#调用带有null的Oracle函数,c#,oracle12c,data-access-layer,odp.net,C#,Oracle12c,Data Access Layer,Odp.net,我试图从我们的C#应用程序调用Oracle函数,但我得到以下错误。我想我有两个问题: 我想调用这个函数,但有些参数在C#端可能为null,因此我不知道如何处理它们 我不知道是否需要使用OracleParameter对象上的ParameterDirection.ReturnValue将返回值添加到参数中 这就是我正在尝试的: public int GetActivityRowCount(DateTime fromDate, DateTime thruDate, string grpCds, str

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

  • 我想调用这个函数,但有些参数在C#端可能为null,因此我不知道如何处理它们

  • 我不知道是否需要使用
    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:调用'FUNC_SEARCHROWCOUNT'时参数的数量或类型错误。


    所有变量都绑定了正确的数量,尽管我在某处读到ODP.NET将删除带有
    null
    的参数,因为那里有
    .Value
    。这是真的吗?我应该传递什么来表示该参数没有值呢?

    您至少需要4件事:

    • 前两个:调用
      ExecuteNonQuery
      和not
      ExecuteScalar
      ,如本文所述,并创建返回值参数。下面三分之一的地方显示了这段代码
    以与存储过程相同的方式执行函数。使用ParameterDirection.ReturnValue参数获取函数返回的结果

    • 第三,一定要使用
      DbNull.Value
      ,因为它是专门设计为数据库中的占位符,而
      null
      仅对.NET有意义。(嗯,
      null
      可能还可以,因为Oracle驱动程序可能足够聪明来处理它;
      DbNull.Value
      是一个好习惯,因为您是显式的)。你可以这样做
    newOracleParameter(“typCds”,OracleDbType.Varchar2,typCds???(object)DbNull.Value,ParameterDirection.Input))

    • 最后,在参数上有bind by name,但是名称与参数的名称不匹配。完全匹配名称或按位置绑定

    至于具体的错误,返回值是“参数”,没有正确绑定参数。Oracle需要13个参数,而您实际上没有提供任何参数。

    您的代码似乎有几个问题。 Oracle类型LONG与C#中的LONG不同,Oracle DB中的LONG允许您存储最大2GB大小的字符数据。在C#中,它是一种使用64位的数字类型。由于您提交的代码没有解释您在包函数的_MemNbr、_SubNbr和_statcs中的参数是什么类型的数据,因此我只能猜测它是什么,这取决于您在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数字类型转换为short(Int16)时,可能会遇到转换/invalidcast异常。当返回的计数大于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);
            }
        }
    

    关于这个主题,我模糊的记忆告诉我,你通过在线阅读传入
    System.DBNull.Value
    @vc74,看起来,
    ExecuteNonQuery
    返回更新数据库时生效的行数,
    ExecuteScalar
    在你只需要一个值时使用,比如
    COUNT(*)
    。关于ExecuteScalar与ExecuteOnQuery,我已经尝试了上述三种方法。使用了
    ExecuteNonQuery
    ,添加到
    ReturnValue
    参数中,还使用了
    运算符检查空值,但我仍然得到相同的错误<代码>PLS-00306:调用'FUNC_SEARCHROWCOUNT'时参数的数量或类型错误。
    我想我可能会看到它,如果正确,我会更新我的答案。参数上有按名称绑定,但名称与参数名称不匹配。请尝试完全匹配名称或按位置绑定。它可能会默认所有参数(计数为零,因为没有一个参数会被绑定)。就是这样!我一直认为,
    BindByName
    只在应用程序的C端使用。我不知道这也适用于你所说的。删除
    BindByName
    修复了它。