Passing Optional & Unordered Parameters via ODP.NET – PLS-00306

Times change. Recently I was interacting with a team that was using ODP.NET. And they had this stored procedure written in SQL server which they were moving to Oracle DB. They had some challenges in porting their data access code to support Oracle. One of them was – ODP.NET requires all parameters (even if they have default values) to be passed to the Stored Procedure and also expects them in the same order. But with SqlClient the same is not required. Solution to this is use BindByName property of OracleCommand class. Let me elaborate through a small sample code for better understanding:

Dummy

Consider the following dummy procedure using the above Dummy Table
CREATE PROCEDURE [dbo].[DummyProc]
@Age As Int,
@Name As varchar(50) = ‘AA’ –default value
AS
BEGIN
SET NOCOUNT ON;
DECLARE @noOfRows as INT

SELECT * from Dummy d where d.Age = @Age AND d.Name = @Name

SELECT @noofrows = @@rowcount –tSql is not case sensitive
RETURN @noofrows
END
Now the C# code to pass parameters to this would be:

SqlConnection conn = new SqlConnection(@”ConnString”);
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = “DummyProc”;
command.Parameters.Add(new SqlParameter(“@Age”, 2)); //Order doesn’t matter
//command.Parameters.Add(new SqlParameter(“@Name”, “AA”)); – This is not required
SqlParameter paramReturnCode = command.CreateParameter();
paramReturnCode.ParameterName = “ReturnCode”;
paramReturnCode.DbType = DbType.Int32;
paramReturnCode.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(paramReturnCode);
command.ExecuteNonQuery();
Console.WriteLine(command.Parameters[@”ReturnCode”].Value);
conn.Close();

//Output
1

Pretty simple. Let’s move the above to oracle now & access it from ODP.NET. First let’s create a stored procedure in Oracle.

create or replace
PROCEDURE DUMMYPROC
( p_Age IN NUMBER
, p_Name IN VARCHAR2 DEFAULT ‘AA’
, p_NoOfRows OUT NUMBER
) AS
BEGIN
select count(*) into p_noofrows from dummy d where d.age = p_Age AND d.name = p_name;
–N.B. In above ‘into’ is required, for select returning a recordset you need a RefCursor
END DUMMYPROC;

Now let’s write some code using ODP.NET (Oracle.DataAccess.dll)

OracleConnection conn = new OracleConnection(@”ConnString”);
conn.Open();
OracleCommand command = conn.CreateCommand();
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = “DummyProc”;
//command.BindByName = true;
command.Parameters.Add(new OracleParameter(“p_Age”, 2));
//command.Parameters.Add(new OracleParameter(“p_Name”, “AA”)); // This is not required
OracleParameter paramReturnCode = command.CreateParameter();
paramReturnCode.ParameterName = “p_NoOfRows”;
paramReturnCode.OracleDbType = OracleDbType.Int32;
paramReturnCode.Direction = ParameterDirection.Output;
command.Parameters.Add(paramReturnCode);
command.ExecuteNonQuery();
Console.WriteLine(command.Parameters[@”p_NoOfRows”].Value);
conn.Close();

The above code on execution throws an error – PLS-00306: wrong number or types of arguments in call to ‘DUMMYPROC’. The issue here is oracle expects all parameters to be passed in same order for faster execution. But as it turns out that it’s not always the case. Run the same Oracle code above but uncomment line no. 7 – command.BindByName = true

And here you are with the expected output.
//Output
1

Hope this helps 🙂 .

8 thoughts on “Passing Optional & Unordered Parameters via ODP.NET – PLS-00306

  1. Thank you very much. It was a great help. I thought it would be much easier in my c# application to change the oracle provider but first I had to change the oracletype enums to oracledbtype enums. I had to apply ToString() to procedure output values before casting to expected .net types because they are not of type object anymore. Optional parameter passing became easy with your article.Well, did it worth it ? Yes. Performs better :))

  2. Very helpful!! I was converting a site from using System.Data.OracleClient to OPD.Net and not all the C# methods had the parameters in the correct order. I was getting very frustrated until finding your article. Very nice! Thanks!

  3. Thank you so much! I could figure out what was going on until I ran across your article. I have to say Oracle approach of changing parameters to be positional (ordered) between by default when switching to Oracle.DataAccess.Client (vs the pre 11g System.Data.OracleClient) seems dangerous and flawed. Is this really the safest approach. Thank goodness my code crashed and I caught this, otherwise I could have had parameters with valid data types that were not ordered and getting set in the wrong fields of my table.

    In addition to this issue (borderline bug in my opinion). The 11.6… version prevents the OracleCommand “Transaction” property from being set (it read-only). This was fixed in 11.7, but both 11.6 and 11.7 have a bug that returns output integer parameters being return by a stored procedure as decimal. This is despite the fact I explicitly state integer parameter an my stored procedure. To get around this I had to the following I had to code the following:

    ImageInfoID = parm.Value.ToInt32

  4. Thank you very much for this article… I could see that parameters were not passed correctly but was not at all sure….Why .. then saw this article and Problem solved.

    Thanks,
    Prashant

Leave a reply to smittrimbakkar Cancel reply