来自托管数据访问的具有多个数组和标量的 Oracle 存储过程

Posted

技术标签:

【中文标题】来自托管数据访问的具有多个数组和标量的 Oracle 存储过程【英文标题】:Oracle stored procedure with multiple arrays and scalars from managed data access 【发布时间】:2018-06-18 13:05:27 【问题描述】:

我正在尝试从 Oracle 托管数据访问客户端调用以下 Oracle 存储过程

PROCEDURE CLONE_PRODUCT(p_f_cloned_prod_id    IN      product.product_id%TYPE,
                        p_f_name              IN      product.name%TYPE,
                        p_f_desc              IN      product.presentation_value%TYPE,
                        p_f_sys_issue         IN      product.product_reference%TYPE,
                        p_f_feature_names     IN      T_CHAR_TAB,
                        p_f_feature_values    IN      T_CHAR_TAB,
                        p_f_audit_user        IN      product.last_updated_by%TYPE,
                        p_f_product_id           OUT  product.product_id%TYPE)

在哪里

TYPE  t_char_tab  IS TABLE OF VARCHAR2(1000) INDEX BY BINARY_INTEGER;

使用此 C# 代码:

 using (var cloneProductCmd = new OracleCommand("SPF_SQL.CLONE_PRODUCT", con))
 
     cloneProductCmd.BindByName = true;
     cloneProductCmd.CommandType = System.Data.CommandType.StoredProcedure;

     cloneProductCmd.Parameters.Add("P_F_CLONED_PROD_ID", 1);
     cloneProductCmd.Parameters.Add("P_F_NAME", "bob");
     cloneProductCmd.Parameters.Add("P_F_DESC", "bob smith");
     cloneProductCmd.Parameters.Add("P_F_SYS_ISSUE", 123);

     var featureNames = new OracleParameter()
     
         ParameterName = "P_F_FEATURE_NAMES",
         Direction = System.Data.ParameterDirection.Input,
         OracleDbType = OracleDbType.Varchar2,
         Value = new string[]  "feature 1" 
     ;
     cloneProductCmd.Parameters.Add(featureNames);

     var featureValues = new OracleParameter()
     
         ParameterName = "P_F_FEATURE_VALUES",
         Direction = System.Data.ParameterDirection.Input,
         OracleDbType = OracleDbType.Varchar2,
         Value = new string[]  "value 1" 
     ;
     cloneProductCmd.Parameters.Add(featureValues);
     cloneProductCmd.Parameters.Add("P_F_AUDIT_USER", "me");
     cloneProductCmd.Parameters.Add("P_F_PRODUCT_ID", OracleDbType.Decimal, System.Data.ParameterDirection.Output);

     cloneProductCmd.ArrayBindCount = 1;

     var reader = await cloneProductCmd.ExecuteNonQueryAsync();

     newProductId = Convert.ToInt32(cloneProductCmd.Parameters["P_F_PRODUCT_ID"].Value.ToString());

我尝试将 ArraybindCount 更改为 2(2 个长度为 1 的数组),还尝试指定数组参数的 collectionTypePLSQLAssociativeArray

我总是收到一条异常消息:

无法将“System.Int32”类型的对象转换为“System.Array”类型

this answer 和 this 文章建议 ArrayBindCount 属性意味着客户端期望所有参数的数组。

我的问题是如何调用一个存储过程,传入多个标量值和多个数组(所有数组都具有相同数量的元素)以及一个输出参数(标量)?

【问题讨论】:

【参考方案1】:

我最终解决了这个问题,在 this 答案的帮助下,我调整了我的代码并让它工作。

简而言之,ArrayBindCount 似乎是不必要的,但是对于每个数组参数,CollectionType, Size, ArrayBindSize and ArrayBindStatus 是必需的,我还通过直接添加到命令的Parameter 集合而不是单独创建它们然后创建参数将它们添加到集合中,不确定是否相关。

这是我的工作代码:

using (var cloneProductCmd = new OracleCommand("SPF_SQL.CLONE_PRODUCT", con))

    cloneProductCmd.BindByName = true;
    cloneProductCmd.CommandType = System.Data.CommandType.StoredProcedure;
    cloneProductCmd.Parameters.Add("P_F_CLONED_PROD_ID", product.OriginalProductId);
    cloneProductCmd.Parameters.Add("P_F_NAME", productName);
    cloneProductCmd.Parameters.Add("P_F_DESC", fullProduct.ProductName);
    cloneProductCmd.Parameters.Add("P_F_SYS_ISSUE", fullProduct.SystemIssueNumber);

    var featureNames = cloneProductCmd.Parameters.Add("P_F_FEATURE_NAMES", OracleDbType.Varchar2);
    featureNames.Direction = System.Data.ParameterDirection.Input;
    featureNames.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    featureNames.Value = features.Select(_ => _.Key).ToArray();
    featureNames.Size = features.Count();
    featureNames.ArrayBindSize = features.Select(_ => _.Key.Length).ToArray();
    featureNames.ArrayBindStatus = Enumerable.Repeat(OracleParameterStatus.Success, features.Count()).ToArray();

    var featureValues = cloneProductCmd.Parameters.Add("P_F_FEATURE_VALUES", OracleDbType.Varchar2);
    featureValues.Direction = System.Data.ParameterDirection.Input;
    featureValues.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    featureValues.Value = features.Select(_ => _.Value).ToArray();
    featureValues.Size = features.Count();
    featureValues.ArrayBindSize = features.Select(_ => _.Value.Length).ToArray();
    featureValues.ArrayBindStatus = Enumerable.Repeat(OracleParameterStatus.Success, features.Count()).ToArray();

    cloneProductCmd.Parameters.Add("P_F_AUDIT_USER", HttpContext.Current.User.Identity.Name);
    cloneProductCmd.Parameters.Add("P_F_PRODUCT_ID", OracleDbType.Decimal, System.Data.ParameterDirection.Output);

    var reader = await cloneProductCmd.ExecuteNonQueryAsync();

    newProductId = Convert.ToInt32(cloneProductCmd.Parameters["P_F_PRODUCT_ID"].Value.ToString());

【讨论】:

以上是关于来自托管数据访问的具有多个数组和标量的 Oracle 存储过程的主要内容,如果未能解决你的问题,请参考以下文章

可以是标量或数组的值的技术术语是啥?

Phpmyadmin 仅接受来自具有多个输入值的 json 数组中的一个条目

Perl:标量,数组,哈希

R语言实战-数据类型-1(标量向量矩阵数组)

将多个表中的多行用于具有标量 UDF 的持久计算列

Qt:如何在模型/视图设置中同步对来自多个线程的数据的访问?