簡體   English   中英

如何通過代碼調用存儲過程(帶有用戶參數)?

[英]How to call a stored procedure (with user parameter) via code?

從我的C#程序中,我需要在Oracle數據庫中調用存儲過程,該數據庫具有以下約定:

PKG_ENTITY.ALTER_ENTITY (VARCHAR2 NAME, VARCHAR2 FULLNAME, ATTRS_TYPE ATTRS, VARCHAR2 STATUS, INTEGER OUT RESULT, VARCHAR2 OUT ERRORMSG). 

RESULTERRORMSG參數是OUT參數。

我知道指定的ATTRS_TYPE類型:

TYPE ATTRS_TYPE IS TABLE OF VARCHAR2(2000) INDEX BY VARCHAR2(30);

我曾經這樣稱呼這個存儲過程:

 private void ExecuteNonQuery(string query, params OracleParameter[] parameters)
 {
      using (var connection = new OracleConnection(_connectionString))
      {
            var command = new OracleCommand(query, connection) { CommandType = CommandType.Text };
            connection.Open();
            command.Parameters.AddRange(parameters);
            command.ExecuteNonQuery();
      }
 }

查詢=

DECLARE
    tNAME varchar2(100);
    tATTRS PKG_ENTITY.ATTRS_TYPE;

    tRESULT INTEGER;
    tERRORMSG varchar2(100);


BEGIN

    tNAME := :pEntityId;

     tATTRS(:pPropId) := :pPropValue;

    PKG_ENTITY.ALTER_ENTITY(tUSERNAME,NULL,tATTRS,NULL,tRESULT,tERRORMSG);
END;

參數值:pEntityId,pPropId和pPropValue在代碼中定義。

一切都很好,但是隨后我收到了必須注銷tRESULT和tERRORMSG參數的值的要求,這給我帶來了很大的困難。 我想通過在調用存儲過程后添加SELECT來修改查詢。 像那樣:

DECLARE
    tNAME varchar2(100);
    tATTRS PKG_ENTITY.ATTRS_TYPE;

    tRESULT INTEGER;
    tERRORMSG varchar2(100);


BEGIN

    tNAME := :pEntityId;

     tATTRS(:pPropId) := :pPropValue;

    PKG_USER.ALTER_USER(tUSERNAME,NULL,tATTRS,NULL,tRESULT,tERRORMSG);
    SELECT tRESULT, tERRORMSG FROM DUAL;
END;

但是從pl/sql語言的角度來看,這樣的查詢是不正確的。 因此,我得出的結論是,我需要直接使用存儲過程調用,並且代碼應如下所示:

private ProcedureResult ExecuteStoredProcedure(string procedureName)
{
    using (var connection = new OracleConnection(_connectionString))
    {
        var command = new OracleCommand(procedureName, connection) { CommandType = CommandType.StoredProcedure };
        connection.Open();
        command.Parameters.Add("NAME", OracleDbType.Varchar2, "New name", ParameterDirection.Input);
        command.Parameters.Add("FULLNAME", OracleDbType.Varchar2, "New fullname", ParameterDirection.Input);
        var attr = new EntityAttribute() { attribute1 = "id", attribute2 = "value"};
        command.Parameters.Add("ATTRS", EntityAttribute, "New fullname", ParameterDirection.Input);
        command.Parameters.Add("STATUS", OracleDbType.Varchar2, "Status", ParameterDirection.Input);
        command.Parameters.Add("RESULT", OracleDbType.Int32).Direction = ParameterDirection.Output;
        command.Parameters.Add("ERRORMSG", OracleDbType.Varchar2).Direction = ParameterDirection.Output;

        command.ExecuteNonQuery();

        return new ProcedureResult()
        {
            StatusCode = int.Parse(command.Parameters["RESULT"].Value.ToString()),
            Message = command.Parameters["ERRORMSG"].Value.ToString()
        };
    }
}

在這里,我在使用PKG_ENTITY.ATTRS_TYPE類型定義時遇到了困難。

TYPE ATTRS_TYPE IS TABLE OF VARCHAR2 (2000) INDEX BY VARCHAR2 (30);

我知道有一個IOracleCustomType接口,但是我不明白如何正確實現它。

例如

[OracleCustomTypeMapping("PKG_ENTITY.ATTRS_TYPE")]
public class EntityAttribute : INullable, IOracleCustomType
{
    [OracleObjectMapping("ATTRIBUTE1")]
    public string attribute1 { get; set; }
    [OracleObjectMapping("ATTRIBUTE2")]
    public string attribute2 { get; set; }

    public bool IsNull => throw new System.NotImplementedException();
    public void FromCustomObject(OracleConnection con, IntPtr pUdt)
    {
        throw new NotImplementedException(); 
    }

    public void ToCustomObject(OracleConnection con, IntPtr pUdt)
    {
        throw new NotImplementedException();
    }
}

此類的字段的名稱應該是什么? 我了解“ ATTRIBUTE1”和“ ATTRIBUTE2”不是有效的名稱。

此答案指出,您無法在C#中傳遞INDEX BY VARCHAR2關聯數組。 相反,您可以在匿名PL / SQL塊中構建關聯數組,然后從那里調用過程(就像您最初所做的那樣)。

因此,您可以使用:

DECLARE
  tATTRS PKG_ENTITY.ATTRS_TYPE;
BEGIN
  tATTRS(:pPropId) := :pPropValue;

  PKG_USER.ALTER_USER(
    NAME     => :pEntityId,
    USERNAME => NULL,
    ATTRS    => tATTRS,
    STATUS   => NULL,
    RESULT   => :pResult,
    ERRORMSG => :pErrorMsg
  );
END;

然后在參數傳遞pPropIdpPropValuepEntityId與方向ParameterDirection.Input ,你在做什么,通過pResultpErrorMsg與方向ParameterDirection.Output

PL存儲過程的簽名應為

PKG_ENTITY.ALTER_ENTITY (VARCHAR2 NAME, VARCHAR2 FULLNAME, ATTRS_TYPE ATTRS, VARCHAR2 STATUS, INTEGER OUT RESULT, VARCHAR2 OUT ERRORMSG)

請注意, OUT已添加到參數聲明中。

c#代碼中,您需要確保將參數標記為輸出

.Parameters.Add("RESULT", OracleDbType.Int32).Direction = ParameterDirection.Output;

完成后,您只需要為PL / SQL存儲過程中的參數分配所需的值即可。

RESULT:= 0;

調用PL / SQL過程要簡單得多(未經測試,但我想您會明白的):

var cmd = new OracleCommand("BEGIN PKG_ENTITY.ALTER_ENTITY(:NAME, :FULLNAME, :ATTRS, :STATUS, :RESULT, :ERRORMSG); END;"), connection);
cmd.CommandType = CommandType.Text;

// should work as well
// var cmd = new SqlCommand("PKG_ENTITY.ALTER_ENTITY(:NAME, :FULLNAME, :ATTRS, :STATUS, :RESULT, :ERRORMSG)"), connection);
// cmd.CommandType = CommandType.StoredProcedure;


cmd.Parameters.Add("NAME", OracleDbType.Varchar2, ParameterDirection.Input).Value = "New name"
cmd.Parameters.Add("FULLNAME", OracleDbType.Varchar2, ParameterDirection.Input).Value = "New fullname"

par = cmd.Parameters.Add("ATTRS", OracleDbType.Varchar2, ParameterDirection.Input);
par.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
var arr string[] = new string[] {"id" , "value"};
par.Value = arr;
par.Size = arr .Count;

cmd.Parameters.Add("STATUS", OracleDbType.Varchar2, ParameterDirection.Input).Value = "Status";
cmd.Parameters.Add("RESULT", OracleDbType.Int32, ParameterDirection.Output);
cmd.Parameters("RESULT").DbType = DbType.Int32;
cmd.Parameters.Add("ERRORMSG", OracleDbType.Varchar2, 100, null, ParameterDirection.Output);
cmd.Parameters("ERRORMSG").DbType = DbType.String;

cmd.ExecuteNonQuery();
var result = System.Convert.ToInt32(cmd.Parameters("RESULT").Value);
var errmsg = cmd.Parameters("ERRORMSG").Value.ToString();

也許這會有所幫助: PL / SQL關聯數組綁定

實際上,我已經不記得為什么我要執行cmd.Parameters("RESULT").DbType = DbType.Int32; 也許需要使我的代碼與ODP.NET Provider 1.x和2.0兼容,請參閱將.NET 1.x存儲過程強制遷移到.NET 2.0或更高版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM